Leveraging Python for QuickBooks API: A Case Study on Invoice Management

By manhnv, at: 18:34 Ngày 15 tháng 3 năm 2024

Thời gian đọc ước tính: 16 min read

Leveraging Python for QuickBooks API: A Case Study on Invoice Management
Leveraging Python for QuickBooks API: A Case Study on Invoice Management

Leveraging Python for QuickBooks API: A Case Study on Invoice Management

 

Introduction

Welcome to this comprehensive guide on how to use Python to interact with the QuickBooks API. In this blog, we will not only introduce the basics of making API calls to QuickBooks with Python, but also walk you through a real-world case study that highlights the power and flexibility of this approach.

Consider this scenario: You are a business owner who uses QuickBooks for book keeping. There are hundreds of invoices monthly. One day, you realize that you need to update a specific detail (ex: tax value) on a large number of invoices. The thought of manually opening each invoice on the QuickBooks main page and making the changes is daunting. You wish there was a way to automate this process.

This is where Python and the QuickBooks API come into play. With these tools, you can write a script that fetches the invoices and makes the necessary changes, all without having to navigate through the QuickBooks UI.

In this guide, we will show you how to set up your environment, authenticate with the QuickBooks API, fetch invoices, and update them. We will cover the basics of the QuickBooks API, error handling, and best practices for structuring your code.

By the end of this guide, you will have a solid understanding of how to use Python to interact with the QuickBooks API, and you will be able to apply this knowledge to solve your own business problems. So, let’s dive in and start our journey towards automation and efficiency!

NOTE: in order to follow the guide eazily, you should have your system installed python and pyenv first You also need to Create a Quickbooks app to retrieve the tokens from that app. If you don’t know how, you can follow this guide.

 

QuickBooks API Overview

The QuickBooks Online API is a RESTful web service that uses OAuth 2.0 for authentication and authorization. It allows third-party applications to interact with the QuickBooks Online platform, enabling them to perform a variety of tasks such as querying data, posting transactions, and more.

The API uses standard HTTP methods like GETPOSTPUT, and DELETE to interact with QuickBooks Online resources. Each resource in QuickBooks Online is associated with an endpoint, and you can perform operations on these resources by making requests to their corresponding endpoints.

Some useful API endpoints to use are:

Vendor

  • Endpoint/v3/company/{companyId}/vendor
  • MethodsGETPOST
  • Description: The Vendor endpoint allows you to manage vendors in your QuickBooks Online company. You can retrieve vendor details, create new vendors, update existing vendors, and delete vendors.

Invoice

  • Endpoint/v3/company/{companyId}/invoice
  • MethodsGETPOST
  • Description: The Invoice endpoint allows you to manage invoices. You can retrieve invoice details, create new invoices, update existing invoices, and delete invoices.

Customer

  • Endpoint/v3/company/{companyId}/customer
  • MethodsGETPOST
  • Description: The Customer endpoint allows you to manage customers in your QuickBooks Online company. You can retrieve customer details, create new customers, update existing customers, and delete customers.

CompanyInfo

  • Endpoint/v3/company/{realm_id}/companyinfo/{realm_id}
  • MethodsGET ,POST
  • Description: The CompanyInfo endpoint allows you to manage and change your Company Information. You can retrieve Company details, full or sparse update your Company

 

Setup the testing environment

First to go, we need to setup our testing environments with the package python-quickbooks

# create the virtual environment for development
pyenv virtualenv 3.10.8 python_quickbooks # you can change 3.10.8 with your installed version of Python
pyenv activate python_quickbooks

# install neccessary dependencies
pip install requests # library for making HTTP(S) request in python
pip install ipython # an interactive shell, alternate to default python shell, much more useful


Now , after all the process are done, you can now access the new shell with command:

ipython

 

Start making API call to Quickbooks

NOTE: In this tutorial, I will use the Invoice endpoint. Before we start to integrate our app with Quickbooks, we first need to generate a access_token to use for calling Quickbooks API. Goto Quickbooks Developer Playground and follow the steps to create a new one.

Type the code below to your shell (or copy them and paste directly to your shell)

import requests

realm_id = "paste your realm id here"
access_token = "paste your access_token here"
query = "select * from Invoice"

sanbox_url = "
<https: sandbox-quickbooks.api.intuit.com="">"
url = f"{sanbox_url}/v3/company/{realm_id}/query?query={query}&minorversion=70"
headers = {
    "Authorization": f"Bearer {access_token}",
    "Accept": "application/json"
}
</https:>

response = requests.get(url, headers=headers)
print(response.json())


The code above are very simple:

  • import requests: This line imports the requests library, which is a popular Python library for making HTTP requests.
     
  • realm_id = "paste your realm id here": This line initializes a variable named realm_id and assigns it a string value "paste your realm id here". You should replace this string with the actual realm ID for your QuickBooks company.
     
  • access_token = "paste your access_token here": This line initializes a variable named access_token and assigns it a string value "paste your access_token here". You should replace this string with the actual access token for your QuickBooks API access.
     
  • query = "select * from Invoice": This line initializes a variable named query and assigns it a SQL-like query string. In this case, it's querying all columns (``) from the Invoice table. This is the query you want to execute on your QuickBooks data.
     
  • sandbox_url = "<https: sandbox-quickbooks.api.intuit.com="">"</https:>: This line initializes a variable named sandbox_url and assigns it the URL of the QuickBooks API sandbox environment. In a real-world scenario, you might use a different URL for the production environment.
     
  • url = f"{sanbox_url}/v3/company/{realm_id}/query?query={query}&minorversion=70": This line constructs the URL for making a query request to the QuickBooks API. It uses an f-string to interpolate the sandbox_url, realm_id, query, and other necessary components to form the complete URL. The minorversion=70 parameter specifies the minor version of the API being used.
     
  • headers = { "Authorization": f"Bearer {access_token}", "Accept": "application/json" }: This line initializes a dictionary named headers containing HTTP headers that will be included in the request. The Authorization header includes the access token necessary for authenticating the request, and the Accept header specifies that the client expects JSON-formatted responses.
     
  • response = requests.get(url, headers=headers): This line makes an HTTP GET request to the URL constructed earlier (url). It includes the headers specified in the headers dictionary. The response from the server is stored in the variable response.
     
  • print(response.json()): This line prints the JSON content of the response received from the QuickBooks API server. The json() method of the response object parses the response content and returns it as a Python dictionary, which is then printed to the console. This should give you the result of the query executed on your QuickBooks data, in JSON format.

After calling the API, you should see the data below printed to your terminal. In case you are wondering, this API responses to you about all the Invoice objects in your Quickbooks account.

{'QueryResponse': {'Invoice': [{'AllowIPNPayment': False,
    'AllowOnlinePayment': False,
    'AllowOnlineCreditCardPayment': False,
    'AllowOnlineACHPayment': False,
    'domain': 'QBO',
    'sparse': False,
    'Id': '159',
    'SyncToken': '1',
    'MetaData': {'CreateTime': '2024-03-13T02:46:50-07:00',
     'LastModifiedByRef': {'value': '9130355782826476'},
     'LastUpdatedTime': '2024-03-13T02:46:50-07:00'},
    'CustomField': [],
    'DocNumber': '1049',
    'TxnDate': '2024-03-13',
    'CurrencyRef': {'value': 'USD', 'name': 'United States Dollar'},
    'LinkedTxn': [{'TxnId': '160', 'TxnType': 'Payment'},
     {'TxnId': '101', 'TxnType': 'Payment'},
     {'TxnId': '31', 'TxnType': 'Payment'},
     {'TxnId': '72', 'TxnType': 'Payment'}],
    'Line': [{'Id': '1',
      'LineNum': 1,
      'Amount': 1111.0,
      'DetailType': 'SalesItemLineDetail',
      'SalesItemLineDetail': {'ItemRef': {'value': '2', 'name': 'Hours'},
       'UnitPrice': 1111,
       'Qty': 1,
       'ItemAccountRef': {'value': '1', 'name': 'Services'},
       'TaxCodeRef': {'value': 'TAX'}}},
     {'Id': '2',
      'LineNum': 2,
      'Description': 'Installation of landscape design',
      'Amount': 50.0,
      'DetailType': 'SalesItemLineDetail',
      'SalesItemLineDetail': {'ItemRef': {'value': '7',
        'name': 'Installation'},
       'UnitPrice': 50,
       'Qty': 1,
       'ItemAccountRef': {'value': '52',
        'name': 'Landscaping Services:Labor:Installation'},
       'TaxCodeRef': {'value': 'TAX'}}},
     {'Amount': 1161.0,
      'DetailType': 'SubTotalLineDetail',
      'SubTotalLineDetail': {}}],
    'TxnTaxDetail': {'TxnTaxCodeRef': {'value': '2'},
     'TotalTax': 92.88,
     'TaxLine': [{'Amount': 92.88,
       'DetailType': 'TaxLineDetail',
       'TaxLineDetail': {'TaxRateRef': {'value': '3'},
        'PercentBased': True,
        'TaxPercent': 8,
        'NetAmountTaxable': 1161.0}}]},
    'CustomerRef': {'value': '1', 'name': "Amy's Bird Sanctuary"},
    'CustomerMemo': {'value': 'Thank you for your business and have a great day!'},
    'BillAddr': {'Id': '2',
     'Line1': '4581 Finch St.',
     'City': 'Bayshore',
     'CountrySubDivisionCode': 'CA',
     'PostalCode': '94326',
     'Lat': 'INVALID',
     'Long': 'INVALID'},
    'ShipAddr': {'Id': '2',
     'Line1': '4581 Finch St.',
     'City': 'Bayshore',
     'CountrySubDivisionCode': 'CA',
     'PostalCode': '94326',
     'Lat': 'INVALID',
     'Long': 'INVALID'},
    'FreeFormAddress': True,
    'SalesTermRef': {'value': '3', 'name': 'Net 30'},
    'DueDate': '2024-04-12',
    'TotalAmt': 1253.88,
    'ApplyTaxAfterDiscount': False,
    'PrintStatus': 'NeedToPrint',
    'EmailStatus': 'NotSet',
    'BillEmail': {'Address': '[email protected]'},
    'Balance': 720.88}],
  'startPosition': 1,
  'maxResults': 1,
  'totalCount': 1},
'time': '2024-03-13T02:47:02.837-07:00'}


Now, you can see that the information about all the invoices in my QuickBooks account is printed in your terminal. As you can see, I only have one Invoice with an ID of ‘159’ and a SyncToken of "1". Now, we should save the invoice ID and SyncToken into variables for later use.

We've examined the GET request to QuickBooks. Next, we should explore how to perform a POST request to QuickBooks. The main purpose is to modify some properties of the Invoice using the API (e.g., PrivateNote, DocNumber…). Just to be clear, in this tutorial, I will update the PrivateNote of the current Invoice.

Let's write some code with slight modifications, this time utilizing the POST method to make the API call.

invoice_id = "the invoice's Id from the previous step"
invoice_sync_token = "the invoice's SyncToken from the previous step"


url = f"{sanbox_url}/v3/company/{realm_id}/invoice?minorversion=70"
data = {
    "SyncToken": invoice_sync_token,
    "Id": invoice_id,
    "sparse": True,
    "PrivateNote": "Private Note",
}
response = requests.post(url, headers=headers, json=data)
print(response.json())

Let's analyze this code a little bit:

  • invoice_id = "the invoice's Id from the previous step": This line initializes a variable named invoice_id and assigns it a string value. This string should represent the ID of the invoice obtained from a previous step or fetched from your data source.
  • invoice_sync_token = "the invoice's SyncToken from the previous step": This line initializes a variable named invoice_sync_token and assigns it a string value. This string should represent the SyncToken of the invoice obtained from a previous step or fetched from your data source.
  • url = f"{sanbox_url}/v3/company/{realm_id}/invoice?minorversion=70": This line constructs the URL for making a POST request to update an invoice in the QuickBooks API. It uses an f-string to interpolate the sandbox_url, realm_id, and other necessary components to form the complete URL. The endpoint /invoice is used to update an invoice.
  • data = { "SyncToken": invoice_sync_token, "Id": invoice_id, "sparse": True, "PrivateNote": "Private Note", }: This line initializes a dictionary named data containing the payload to be sent in the request body. To be more specific:
    • SyncToken: the invoice’s SyncToken to determine that the versions of Invoice that you’re going to update is the newest version
    • Id: the invoice’s Id to determine which Invoice you want to update
    • sparse: a boolean variable to let QuickBooks know that you only want to update some of the Invoice information, not the whole Invoice
    • PrivateNote: the attribute and its value of the Invoice that you want to update
  • response = requests.post(url, headers=headers, json=data): This line makes an HTTP POST request to the URL constructed earlier (url). It includes the headers specified in the headers dictionary and the payload specified in the data dictionary as JSON data. This request is used to update the invoice with the provided data.
  • print(response.json()): This line prints the JSON content of the response received from the QuickBooks API server. The json() method of the response object parses the response content and returns it as a Python dictionary, which is then printed to the console. This should give you the response of the invoice update operation, including any errors or success messages.

Hence, after executed the code above, you should see the following response from the terminal:

{'Invoice': {'AllowIPNPayment': False,
  'AllowOnlinePayment': False,
  'AllowOnlineCreditCardPayment': False,
  'AllowOnlineACHPayment': False,
  'InvoiceLink': '<https: comingsoonview="" developer.intuit.com="" scs-v1-494225558c3846d1a24e8e06e6cd613f29e0f7d6b3844af1a22d00fd397dd424c6d545d13ecd4c50b0dde49772cb5b17="">',
  'domain': 'QBO',
  'sparse': False,
  'Id': '159',
  'SyncToken': '2',
  'MetaData': {'CreateTime': '2024-03-13T02:46:50-07:00',
   'LastModifiedByRef': {'value': '9130355782826476'},
   'LastUpdatedTime': '2024-03-13T05:05:05-07:00'},
  'CustomField': [],
  'DocNumber': '1049',
  'TxnDate': '2024-03-13',
  'CurrencyRef': {'value': 'USD', 'name': 'United States Dollar'},
  'PrivateNote': 'Private Note',
  'LinkedTxn': [{'TxnId': '160', 'TxnType': 'Payment'},
   {'TxnId': '101', 'TxnType': 'Payment'},
   {'TxnId': '31', 'TxnType': 'Payment'},
   {'TxnId': '72', 'TxnType': 'Payment'}],
  'Line': [{'Id': '1',
    'LineNum': 1,
    'Amount': 1111.0,
    'DetailType': 'SalesItemLineDetail',
    'SalesItemLineDetail': {'ItemRef': {'value': '2', 'name': 'Hours'},
     'UnitPrice': 1111,
     'Qty': 1,
     'ItemAccountRef': {'value': '1', 'name': 'Services'},
     'TaxCodeRef': {'value': 'TAX'}}},
   {'Id': '2',
    'LineNum': 2,
    'Description': 'Installation of landscape design',
    'Amount': 50.0,
    'DetailType': 'SalesItemLineDetail',
    'SalesItemLineDetail': {'ItemRef': {'value': '7', 'name': 'Installation'},
     'UnitPrice': 50,
     'Qty': 1,
     'ItemAccountRef': {'value': '52',
      'name': 'Landscaping Services:Labor:Installation'},
     'TaxCodeRef': {'value': 'TAX'}}},
   {'Amount': 1161.0,
    'DetailType': 'SubTotalLineDetail',
    'SubTotalLineDetail': {}}],
  'TxnTaxDetail': {'TxnTaxCodeRef': {'value': '2'},
   'TotalTax': 92.88,
   'TaxLine': [{'Amount': 92.88,
     'DetailType': 'TaxLineDetail',
     'TaxLineDetail': {'TaxRateRef': {'value': '3'},
      'PercentBased': True,
      'TaxPercent': 8,
      'NetAmountTaxable': 1161.0}}]},
  'CustomerRef': {'value': '1', 'name': "Amy's Bird Sanctuary"},
  'CustomerMemo': {'value': 'Thank you for your business and have a great day!'},
  'BillAddr': {'Id': '2',
   'Line1': '4581 Finch St.',
   'City': 'Bayshore',
   'CountrySubDivisionCode': 'CA',
   'PostalCode': '94326',
   'Lat': 'INVALID',
   'Long': 'INVALID'},
  'ShipAddr': {'Id': '2',
   'Line1': '4581 Finch St.',
   'City': 'Bayshore',
   'CountrySubDivisionCode': 'CA',
   'PostalCode': '94326',
   'Lat': 'INVALID',
   'Long': 'INVALID'},
  'FreeFormAddress': True,
  'SalesTermRef': {'value': '3', 'name': 'Net 30'},
  'DueDate': '2015-09-30',
  'TotalAmt': 1253.88,
  'ApplyTaxAfterDiscount': False,
  'PrintStatus': 'NeedToPrint',
  'EmailStatus': 'NotSet',
  'BillEmail': {'Address': '[email protected]'},
  'Balance': 720.88},
'time': '2024-03-13T05:05:05.027-07:00'}</https:>


Here, you can see that my Invoice now has the PrivateNote attribute and its value is “Private Note”. The SyncToken also change from “1” to “2”, show that you’ve update the Invoice, and now the newest version of the Invoice is version “2”.

 

Conclusion

If you read this far, congratulations, you now know how to make API calls to Quickbooks using python. This is a very useful knowledge and we can apply this to many other things, for example integrating Django App with Quickbooks (this will also be something I will talk about in the next blog ^^ ).

Happy Coding!

 


Theo dõi

Theo dõi bản tin của chúng tôi và không bao giờ bỏ lỡ những tin tức mới nhất.