Skip to content

dveldhoen/easyinvoice-pip

Repository files navigation

Easy Invoice

A product by

Easy Invoice logo

Build for Web and Backend 💪


Version Build Status Coverage Status
Downloads License Pull Request's Welcome

If this package helped you out please star us on Github!
Much appreciated!

Pull Request's Welcome

Important

  1. Please note that this package is a wrapper for an API, so it's logic runs on external servers.
  2. Your data is secure and will not be shared with third parties.
  3. We try to keep the API up and running at all times, but we cannot guarantee 100% uptime. Please build in a retry mechanism in case the API is down for maintenance.

Installing

Using PIP:

$ pip install easyinvoice

Using PIP3:

$ pip3 install easyinvoice

Platform support

Platform Repository Supported Link
PHP Composer Yes! Available on Composer
Javascript NPM Yes! Available on NPM
Python PIP Yes! Available on PIP

Sample


Easy Invoice Sample Logo Only Easy Invoice Sample With Background

JSON Configs used for above samples:

Plans

Plan Rate Price Link
Free 25 invoices / 15 days $0 Not required to register
Paid Unlimited - 30 day free trial
- 1st month $1.99
- $17.99 per month
*Prices include VAT
Register here

To use paid

  1. Register through:
  2. Create an API key through the app: settings -> API keys
  3. Use the API Key as shown in the complete example below. Add the apiKey property to the data object.

Note: The GUI is not (yet) fully translated to English, though the path to getting an apiKey should mostly be in English. Also this will allow you to use the in app purchase mechanism to pay for the subscription.

Development mode

When using the free version, you can set the mode to 'development' to make sure you are not running into rate limits while testing this package or developing your invoices. The free version is limited to 25 invoices per 15 days. When your invoice looks good, you can switch to 'production' mode to create your production invoices. Production mode is activated by either not setting the mode or setting the mode to 'production'.

Direct REST API access

In case you don't want to use the pip package, but you want to call our invoice creation api directly.

# HTTPS POST 
https://api.budgetinvoice.com/v2/free/invoices

# POST Data
Format: JSON
Structure: {
  "data": { # Parent parameter must be 'data'
    "mode": "development", # Production or development, defaults to production
    "products": [
      {
        "quantity": 2,
        "description": "Test product",
        "taxRate": 6,
        "price": 33.87
      }
    ],
  }
} 

# Optionally add your paid apiKey to the header 
Header: "Authorization": "Bearer 123abc" # Please register to receive a production apiKey: https://app.budgetinvoice.com/register

Import

# Import the EasyInvoice library
from easyinvoice import EasyInvoice

Getting Started - Basic Example

# Import the EasyInvoice library
from easyinvoice import EasyInvoice

# Create a dictionary with the settings to create the invoice
data = {
    "apiKey": "free", # Please register to receive a production apiKey: https://app.budgetinvoice.com/register
    "mode": "development", # Production or development, defaults to production
    "products": [
        {
            "quantity": 2,
            "description": "Test product",
            "taxRate": 6,
            "price": 33.87
        }
    ]
}

# This will return the PDF as base64 string
result = EasyInvoice.create(data)

# To save the PDF locally call the save function
EasyInvoice.save(result["pdf"])

High volumes: asynchronous invoice creation

Our API is able to handle high volumes of requests. If you need to create a lot of invoices fast, make sure to create them asynchronously. This will allow you to create multiple invoices at the same time.

import asyncio
from easyinvoice import EasyInvoice
import asyncio

async def create_invoice_async():
    data = {
        "apiKey": "free",
        "mode": "development",
        "products": [
            {
                "quantity": 2,
                "description": "Test product",
                "taxRate": 6,
                "price": 33.87
            }
        ]
    }
    # Use asyncio.to_thread to run the synchronous EasyInvoice.create in a separate thread
    result = await asyncio.to_thread(EasyInvoice.create, data)
    return result["pdf"]

async def main():
    # Schedule asynchronous execution of create_invoice_async for multiple invoices
    tasks = [create_invoice_async() for _ in range(25)]
    invoices = await asyncio.gather(*tasks)
    return invoices

if __name__ == "__main__":
    all_invoices = asyncio.run(main())
    print(all_invoices)

Full Example

# Import the EasyInvoice library
from easyinvoice import EasyInvoice

# Data will contain all the information we would like to see on our invoice
data = {
    "apiKey": "free", # Please register to receive a production apiKey: https://app.budgetinvoice.com/register
    "mode": "development", # Production or development, defaults to production    
    "images": {
        # The logo on top of your invoice
        "logo": "https://public.easyinvoice.cloud/img/logo_en_original.png",
        # The invoice background
        "background": "https://public.easyinvoice.cloud/img/watermark-draft.jpg"
    },
    # Your own data
    "sender": {
        "company": "Sample Corp",
        "address": "Sample Street 123",
        "zip": "1234 AB",
        "city": "Sampletown",
        "country": "Samplecountry"
        # "custom1": "sender-custom1",
        # "custom2": "sender-custom2",
        # "custom3": "sender-custom3"
    },
    # Your recipient
    "client": {
        "company": "Client Corp",
        "address": "Clientstreet 456",
        "zip": "4567 CD",
        "city": "Clientcity",
        "country": "Clientcountry"
        # "custom1": "client-custom1",
        # "custom2": "client-custom2",
        # "custom3": "client-custom3"
    },
    "information": {
        # Invoice number
        "number": "2021.0001",
        # Invoice data
        "date": "12-12-2021",
        # Invoice due date
        "dueDate": "31-12-2021"
    },
    # The products you would like to see on your invoice
    # Total values are being calculated automatically
    "products": [
        {
            "quantity": 2,
            "description": "Test2",
            "taxRate": 6,
            "price": 33.87
        },
        {
            "quantity": 4.1,
            "description": "Test1",
            "taxRate": 6,
            "price": 12.34
        },
        {
            "quantity": 4.5678,
            "description": "Test2",
            "taxRate": 21,
            "price": 6324.453456
        }
    ],
    # The message you would like to display on the bottom of your invoice
    "bottomNotice": "Kindly pay your invoice within 15 days.",
    # Settings to customize your invoice
    "settings": {
        "currency": "USD", # See documentation 'Locales and Currency' for more info. Leave empty for no currency.
        #     "locale": "nl-NL", # Defaults to en-US, used for number formatting (See documentation 'Locales and Currency')
        #     "margin-top": 25, # Defaults to '25'
        #     "margin-right": 25, # Defaults to '25'
        #     "margin-left": 25, # Defaults to '25'
        #     "margin-bottom": 25, # Defaults to '25'
        #     "format": "A4", # Defaults to A4, options: A3, A4, A5, Legal, Letter, Tabloid
        #     "height": "1000px", // allowed units: mm, cm, in, px
        #     "width": "500px", // allowed units: mm, cm, in, px
        #     "orientation": "landscape" // portrait or landscape, defaults to portrait   
    },
    # Translate your invoice to your preferred language
    "translate": {
        #     "invoice": "FACTUUR",  # Default to 'INVOICE'
        #     "number": "Nummer", # Defaults to 'Number'
        #     "date": "Datum", # Default to 'Date'
        #     "dueDate": "Verloopdatum", # Defaults to 'Due Date'
        #     "subtotal": "Subtotaal", # Defaults to 'Subtotal'
        #     "products": "Producten", # Defaults to 'Products'
        #     "quantity": "Aantal", # Default to 'Quantity'
        #     "price": "Prijs", # Defaults to 'Price'
        #     "productTotal": "Totaal", # Defaults to 'Total'
        #     "total": "Totaal", # Defaults to 'Total'
        #     "taxNotation": "btw" // Defaults to 'vat'
    },
    # Customize enables you to provide your own templates
    # Please review the documentation for instructions and examples
    # "customize": {
    #  "template": base64.b64encode("<p>Hello World!</p>".encode()) // Must be base64 encoded html. This example contains 'Hello World!' in base64  
    # },
}

# Returns a dict containing all the data of the invoice
result = EasyInvoice.create(data)

# Store the pdf locally
EasyInvoice.save(result["pdf"], 'myInvoice')

Return values

Key Value Data Type
result["pdf"] The PDF file as base64 string String
result["calculations"]["products"] Array of the products used in creation Array
result["calculations"]["products"][key]["subtotal"] Rounded price without tax per product Number
result["calculations"]["products"][key]["tax"] Rounded tax per product Number
result["calculations"]["products"][key]["total"] Rounded price including tax per product Number
result["calculations"]["tax"] Array of objects containing total calculated tax per unique tax rate Array
result["calculations"]["tax"][rate] Total tax for all products with same tax rate Number
result["calculations"]["subtotal"] Rounded price without tax for all products Number
result["calculations"]["total"] Rounded price without tax for all products Number

Error handling

try:
    # Import the EasyInvoice library
    from easyinvoice import EasyInvoice
    
    # Create a dictionary with the settings to create the invoice
    data = {
        "apiKey": "free", # Please register to receive a production apiKey: https://app.budgetinvoice.com/register
        "mode": "development", # Production or development, defaults to production
        "products": [
            {
                "quantity": 2,
                "description": "Test product",
                "taxRate": 6,
                "price": 33.87
            }
        ]
    }
    
    # Returns a dict containing all the data of the invoice
    result = EasyInvoice.create(data)

    # Store the pdf locally
    EasyInvoice.save(result["pdf"], 'invoice')
except Exception as e:
    # If an error occurs, it will be caught here
    print(e)

Locales and Currency

Used for number formatting and the currency symbol:

# E.g. for Germany, prices would look like 123.456,78 €
data = {
    "apiKey": "free", # Please register to receive a production apiKey: https://app.budgetinvoice.com/register
    "mode": "development", # Production or development, defaults to production
    "settings":
        {
            "locale": "de-DE",
            "currency": "EUR"
        }
}

# E.g. for US, prices would look like $123,456.78
data = {
    "apiKey": "free", # Please register to receive a production apiKey: https://app.budgetinvoice.com/register
    "mode": "development", # Production or development, defaults to production
    "settings":
        {
            "locale": "en-US",
            "currency": "USD"
        }
}

Formatting and symbols are applied through the ECMAScript Internationalization API

Click here for a list of locale codes
Click here for a list of currency codes

Disclaimer: Not all locales and currency codes found in the above lists might be supported by the ECMAScript Internationalization API.

Logo and Background

The logo and url inputs accept either a URL or a base64 encoded file.

Supported file types:

  • Logo: image
  • Background: image, pdf

URL

data = {    
    "apiKey": "free", # Please register to receive a production apiKey: https://app.budgetinvoice.com/register
    "mode": "development", # Production or development, defaults to production
    "images": {
        "logo": "https://public.easyinvoice.cloud/img/logo_en_original.png",
        "background": "https://public.easyinvoice.cloud/img/watermark_draft.jpg"
    }
}

Base64

data = {    
    "apiKey": "free", # Please register to receive a production apiKey: https://app.budgetinvoice.com/register
    "mode": "development", # Production or development, defaults to production
    # Note: Sample base64 string
    # Please use the link below to convert your image to base64
    "images": {
        "logo": "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
        "background": "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
    }
}

Click here for an online tool to convert an image to base64

Template customization

Download our default template (invoice-v2) here to have an example which you can customize.

Supported file types:

  • Base64
  • URL (soon)
# You are able to provide your own html template
html = "<p>Hello world! This is invoice number %number%</p>"

data = {    
    "apiKey": "free", # Please register to receive a production apiKey: https://app.budgetinvoice.com/register
    "mode": "development", # Production or development, defaults to production
    "customize": {
        # Your template needs to be base64 encoded
        "template": base64.b64encode(html)
    },
    "settings": {
        "number": '2022.0001'
    }
}

EasyInvoice.create(data)

# This will return a pdf with the following content
# Hello world! This is invoice number 2022.0001

Variable placeholders

The following placeholders can be put into your template. They will be replaced by their corresponding value upon creation.

Placeholder Will be replaced by
%document-title% translate.invoice
%logo% images.logo
%company-from% sender.company
%address-from% sender.address
%zip-from% sender.zip
%city-from% sender.city
%country-from% sender.country
%sender-custom-1% sender.custom1
%sender-custom-2% sender.custom2
%sender-custom-3% sender.custom3
%company-to% client.company
%address-to% client.address
%zip-to% client.zip
%city-to% client.city
%country-to% client.country
%client-custom-1% client.custom1
%client-custom-2% client.custom2
%client-custom-3% client.custom3
%number-title% translate.number
%number% settings.number
%date-title% translate.date
%date% settings.date
%due-date-title% translate.dueDate
%due-date% settings.dueDate
%products-header-products% translate.products
%products-header-quantity% translate.quantity
%products-header-price% translate.price
%products-header-total% translate.productTotal
A custom product row must be enclosed in products tags like:
<products>
    <!-- Product row html -->
</products>

Don't leave out the product tags or your custom product row won't be iterable by the template parser and you will end up with a single product row. Customize the html as you wish.

products
Within: <products></products>

%description%

products[].description
Within: <products></products>

%quantity%

products[].quantity
Within: <products></products>

%price%

products[].price
Within: <products></products>

%row-total%

products[].quantity * products[].price (rounded)
%subtotal-title% translate.subtotal
%subtotal% Auto inserted:
Calculated total price excluding tax
A custom tax row must be enclosed in tax tags like:
<tax>
    <!-- Tax row html -->
</tax>

Don't leave out the tax tags or your custom tax row won't be iterable by the template parser and you will end up with a single tax row. Customize the html as you wish.

tax
Within: <tax></tax>

%tax-notation%

settings.taxNotation
Within: <tax></tax>

%tax-rate%

Auto inserted:
Distinct tax rate used in products
Within: <tax></tax>

%tax%

Auto inserted:
Calculated total tax for rate
%total% Auto inserted:
Calculated total price including tax

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages