Giter VIP home page Giter VIP logo

python-quickbooks's Introduction

python-quickbooks

Python package codecov PyPI

A Python 3 library for accessing the Quickbooks API. Complete rework of quickbooks-python.

These instructions were written for a Django application. Make sure to change it to whatever framework/method you’re using. You can find additional examples of usage in Integration tests folder.

For information about contributing, see the Contributing Page.

Installation

pip install python-quickbooks

QuickBooks OAuth

This library requires intuit-oauth. Follow the OAuth 2.0 Guide for installation and to get connected to QuickBooks API.

Accessing the API

Set up an AuthClient passing in your CLIENT_ID and CLIENT_SECRET.

from intuitlib.client import AuthClient

auth_client = AuthClient(
        client_id='CLIENT_ID',
        client_secret='CLIENT_SECRET',
        access_token='ACCESS_TOKEN',  # If you do not pass this in, the Quickbooks client will call refresh and get a new access token. 
        environment='sandbox',
        redirect_uri='http://localhost:8000/callback',
    )

Then create a QuickBooks client object passing in the AuthClient, refresh token, and company id:

from quickbooks import QuickBooks

client = QuickBooks(
        auth_client=auth_client,
        refresh_token='REFRESH_TOKEN',
        company_id='COMPANY_ID',
    )

If you need to access a minor version (See Minor versions for details) pass in minorversion when setting up the client:

client = QuickBooks(
    auth_client=auth_client,
    refresh_token='REFRESH_TOKEN',
    company_id='COMPANY_ID',
    minorversion=69
)

Object Operations

List of objects:

from quickbooks.objects.customer import Customer
customers = Customer.all(qb=client)

Note: The maximum number of entities that can be returned in a response is 1000. If the result size is not specified, the default number is 100. (See Query operations and syntax for details)

Warning: You should never allow user input to pass into a query without sanitizing it first! This library DOES NOT sanitize user input!

Filtered list of objects:

customers = Customer.filter(Active=True, FamilyName="Smith", qb=client)

Filtered list of objects with ordering:

# Get customer invoices ordered by TxnDate
invoices = Invoice.filter(CustomerRef='100', order_by='TxnDate', qb=client)

# Same, but in reverse order
invoices = Invoice.filter(CustomerRef='100', order_by='TxnDate DESC', qb=client)

# Order customers by FamilyName then by GivenName
customers = Customer.all(order_by='FamilyName, GivenName', qb=client)

Filtered list of objects with paging:

customers = Customer.filter(start_position=1, max_results=25, Active=True, FamilyName="Smith", qb=client)

List Filtered by values in list:

customer_names = ['Customer1', 'Customer2', 'Customer3']
customers = Customer.choose(customer_names, field="DisplayName", qb=client)

List with custom Where Clause (do not include the "WHERE"):

customers = Customer.where("Active = True AND CompanyName LIKE 'S%'", qb=client)

List with custom Where and ordering

customers = Customer.where("Active = True AND CompanyName LIKE 'S%'", order_by='DisplayName', qb=client)

List with custom Where Clause and paging:

customers = Customer.where("CompanyName LIKE 'S%'", start_position=1, max_results=25, qb=client)

Filtering a list with a custom query (See Query operations and syntax for supported SQL statements):

customers = Customer.query("SELECT * FROM Customer WHERE Active = True", qb=client)

Filtering a list with a custom query with paging:

customers = Customer.query("SELECT * FROM Customer WHERE Active = True STARTPOSITION 1 MAXRESULTS 25", qb=client)

Get record count (do not include the "WHERE"):

customer_count = Customer.count("Active = True AND CompanyName LIKE 'S%'", qb=client)

Get single object by Id and update:

customer = Customer.get(1, qb=client)
customer.CompanyName = "New Test Company Name"
customer.save(qb=client)

Create new object:

customer = Customer()
customer.CompanyName = "Test Company"
customer.save(qb=client)

Batch Operations

The batch operation enables an application to perform multiple operations in a single request (See Intuit Batch Operations Guide for full details).

Batch create a list of objects:

from quickbooks.batch import batch_create

customer1 = Customer()
customer1.CompanyName = "Test Company 1"

customer2 = Customer()
customer2.CompanyName = "Test Company 2"

customers = [customer1, customer2]

results = batch_create(customers, qb=client)

Batch update a list of objects:

from quickbooks.batch import batch_update
customers = Customer.filter(Active=True)

# Update customer records

results = batch_update(customers, qb=client)

Batch delete a list of objects (only entities that support delete can use batch delete):

from quickbooks.batch import batch_delete

payments = Payment.filter(TxnDate=date.today())
results = batch_delete(payments, qb=client)

Review results for batch operation:

# successes is a list of objects that were successfully updated
for obj in results.successes:
   print("Updated " + obj.DisplayName)

# faults contains list of failed operations and associated errors
for fault in results.faults:
   print("Operation failed on " + fault.original_object.DisplayName)

   for error in fault.Error:
       print("Error " + error.Message)

Change Data Capture

Change Data Capture returns a list of objects that have changed since a given time (see Change data capture for more details):

from quickbooks.cdc import change_data_capture
from quickbooks.objects import Invoice

cdc_response = change_data_capture([Invoice], "2017-01-01T00:00:00", qb=client)
for invoice in cdc_response.Invoice:
   # Do something with the invoice

Querying muliple entity types at the same time:

from quickbooks.objects import Invoice, Customer
cdc_response = change_data_capture([Invoice, Customer], "2017-01-01T00:00:00", qb=client)

If you use a datetime object for the timestamp, it is automatically converted to a string:

from datetime import datetime

cdc_response = change_data_capture([Invoice, Customer], datetime(2017, 1, 1, 0, 0, 0), qb=client)

Attachments

See Attachable documentation for list of valid file types, file size limits and other restrictions.

Attaching a note to a customer:

attachment = Attachable()

attachable_ref = AttachableRef()
attachable_ref.EntityRef = customer.to_ref()

attachment.AttachableRef.append(attachable_ref)

attachment.Note = 'This is a note'
attachment.save(qb=client)

Attaching a file to customer:

attachment = Attachable()

attachable_ref = AttachableRef()
attachable_ref.EntityRef = customer.to_ref()

attachment.AttachableRef.append(attachable_ref)

attachment.FileName = 'Filename'
attachment._FilePath = '/folder/filename'  # full path to file
attachment.ContentType = 'application/pdf'
attachment.save(qb=client)

Passing in optional params

Some QBO objects have options that need to be set on the query string of an API call. One example is include=allowduplicatedocnum on the Purchase object. You can add these params when calling save:

purchase.save(qb=self.qb_client, params={'include': 'allowduplicatedocnum'})

Sharable Invoice Link

To add a sharable link for an invoice, make sure the AllowOnlineCreditCardPayment is set to True and BillEmail is set to a invalid email address:

invoice.AllowOnlineCreditCardPayment = True
invoice.BillEmail = EmailAddress()
invoice.BillEmail.Address = '[email protected]'

When you query the invoice include the following params (minorversion must be set to 36 or greater):

invoice = Invoice.get(id, qb=self.qb_client, params={'include': 'invoiceLink'})

Void an invoice

Call void on any invoice with an Id:

invoice = Invoice()
invoice.Id = 7
invoice.void(qb=client)

Working with JSON data

All objects include to_json and from_json methods.

Converting an object to JSON data:

account = Account.get(1, qb=client)
json_data = account.to_json()

Loading JSON data into a quickbooks object:

account = Account.from_json(
 {
  "AccountType": "Accounts Receivable",
  "AcctNum": "123123",
  "Name": "MyJobs"
 }
)
account.save(qb=client)

Date formatting

When setting date or datetime fields, Quickbooks requires a specific format. Formating helpers are available in helpers.py. Example usage:

date_string = qb_date_format(date(2016, 7, 22))
date_time_string = qb_datetime_format(datetime(2016, 7, 22, 10, 35, 00))
date_time_with_utc_string = qb_datetime_utc_offset_format(datetime(2016, 7, 22, 10, 35, 00), '-06:00')

Exception Handling

The QuickbooksException object contains additional QBO error code information.

from quickbooks.exceptions import QuickbooksException

try:
    # perform a Quickbooks operation
except QuickbooksException as e:
    e.message # contains the error message returned from QBO
    e.error_code # contains the  
    e.detail # contains additional information when available  

Note: Objects and object property names match their Quickbooks counterparts and do not follow PEP8.

Note: This is a work-in-progress made public to help other developers access the QuickBooks API. Built for a Django project.

python-quickbooks's People

Contributors

bendavis78 avatar calvinleenyc avatar chandeeland avatar cstarner avatar ej2 avatar fabiofleitas avatar fuhrysteve avatar grantmcconnaughey avatar humrochagf avatar jakubczaplicki avatar jaredthecoder avatar jstacoder avatar kgrover avatar laf-rge avatar mbierma avatar nbhambhani avatar pablogamboa avatar peterlai avatar pkochubey avatar porn avatar pvizeli avatar redblacktree avatar ridwan-kazi avatar rubinovitz avatar simonv3 avatar subodh-malgonde avatar troolee avatar tusky avatar wowkin2 avatar zedobaia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-quickbooks's Issues

Throttle Exceeded error incorrectly reported

When QuickBook's rate limit is exceeded the client receives the error response but raises the exception for when it cannot read the json response instead of reporting that the request limit has been reached. Provided below is the exception thrown when this occurs:

QuickbooksException: Error reading json response: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse time="2016-10-10T12:14:28.801-07:00" xmlns="http://schema.intuit.com/finance/v3">
    <Fault type="SERVICE">
        <Error code="3001">
            <Message>message=ThrottleExceeded; errorCode=003001; statusCode=403</Message>
            <Detail>The request limit was reached.  Try again later</Detail>
        </Error>
    </Fault>
</IntuitResponse>

It looks like this is happening because the make_request method in the QuickBooks object expects both success and error response to return json but for the ThrottleExceeded error QuickBooks responds with xml.

I am using version 0.5.1 of the library and a sandbox instance on QuickBooks.

Reports API

To implement reports, you can add this function to client.py:

def get_report(self, report_type, qs):
        url = self.api_url + "/company/{0}/reports/{1}".format(self.company_id, report_type)
        result = self.make_request("GET", url, params=qs)
        return result

and make two other changes to make_request:
[1]

def make_request(self, request_type, url, request_body=None, content_type='application/json', params=None):

[2]

if not params:
            params = {}

(If I knew how to do a pull request, I would. Thanks for building this!)

Here's my sample function that handles a P+L report response. This is not for production, just to save you some time when you get around to it.

self._pldataset = {
        'dates': [],
        'data': []
    }

 pldata = QuickBooksExtended.get_report(self._self, 'ProfitAndLoss', {
            'date_macro' : 'This Fiscal Year',
            'accounting_method' : 'Cash',
            'summarize_column_by': 'Month'
            })

self._massagePLRowData(pldata)  <-- recursive, and relies on member var for storage 
self._savePLRowData()   <-- just dumps into database


# note: very recursive...
def _massagePLRowData(self, pileOfRows):

    if isinstance(pileOfRows, list):
        for x in pileOfRows:
            self._getPLRowData(x)

    else:

        if 'Rows' in pileOfRows.keys():
            self._getPLRowData(pileOfRows['Rows'])

        if 'Row' in pileOfRows.keys():
            self._getPLRowData(pileOfRows['Row'])

        if 'Summary' in pileOfRows.keys():
            self._getPLRowData(pileOfRows['Summary'])

        if 'ColData' in pileOfRows.keys():
            d = dict()
            d['name'] = pileOfRows['ColData'][0]['value']
            vals = []
            for x in pileOfRows['ColData'][1:]:
                vals.append(x['value'])
            d['values'] = vals
            self._pldataset['data'].append(d)

        if 'Columns' in pileOfRows.keys():
            self._getPLRowData(pileOfRows['Columns'])

        if 'Column' in pileOfRows.keys():
            for x in pileOfRows['Column']:
                if 'MetaData' in x.keys():
                    self._pldataset['dates'].append(x['MetaData'][0]['Value'])

Error in CashBackInfo class_dict

In deposits.py, line 28:

    class_dict = {
        "AccountRef": None
    }

should read:

    class_dict = {
        "AccountRef": Ref
    }

as currently written, when trying to process a deposit, it attempts to instantiate a None object instead of a Ref during from_json() (also) line 28:

sub_obj = obj.class_dict[key]()

AttributeError: 'tuple' object has no attribute 'items'

Hey guys,

So I have been trying to use your python package but I keep running into this attribute error. For example, when I run customer = Customer.get(1, qb=client) it gives me the error stated in the title.

It will work when I do customers = Customer.all(qb=client) though.

Any ideas on why?

"IndexError: list index out of range" at batch.py

Hello.

I have created around 100 000 object before an error below.
Do you have idea what could be wrong?

Traceback (most recent call last):
  File "bridge.py", line 318, in <module>
    createTransactions()
  File "bridge.py", line 308, in createTransactions
    sendBatch(create_list, command='create')
  File "bridge.py", line 178, in sendBatch
    results = batch_create(batch_list, qb=client)
  File "C:\Users\crs\AppData\Local\Programs\Python\Python35\lib\site-packages\quickbooks\batch.py", line 81, in batch_create
    return batch_mgr.save(obj_list, qb=qb)
  File "C:\Users\crs\AppData\Local\Programs\Python\Python35\lib\site-packages\quickbooks\batch.py", line 23, in save
    result = self.process_batch(temp_list, qb=qb)
  File "C:\Users\crs\AppData\Local\Programs\Python\Python35\lib\site-packages\quickbooks\batch.py", line 38, in process_batch
    batch_response = self.batch_results_to_list(json_data, batch, obj_list)
  File "C:\Users\crs\AppData\Local\Programs\Python\Python35\lib\site-packages\quickbooks\batch.py", line 62, in batch_results_to_list
    batch_item = [obj for obj in batch.BatchItemRequest if obj.bId == response_item.bId][0]
IndexError: list index out of range

Singleton Pattern Dangerous?

I love what you've done here, and I'm trying to use this in a django project, but the singleton pattern really worries me.

Say you have 2 different users that each connect to their own QuickBooks account. Say those 2 users connect to the django website at roughly same time. The first user who hits the page, will connect his quickbooks account, and will be about to do some operation. The second user will then need to connect his quickbooks account, which then changes the tokens on the singleton object to his own tokens. If the first user is just about to do his operation, all of a sudden, that operation will be done on the second user's quickbooks account. VERY BAD.

Am I misunderstanding this? As far as I know, you almost never want to have global variables in a django project because django is a long-running process. Those global variables will stick around between requests, AND between users/sessions.

I am thinking about forking this project, and refactoring it to not use singletons, because I love how everything else in the project is done.

build_choose_clause unicode issue

I've recently run into an issue doing Customer.choose with unicode strings which stem from the build_choose_clause function. If I pass in a unicode string with a unicode specific character it will throw a UnicodeEncodeError when it tries to format the string. This would probably happen with build_where_clause as well since it works similarly.

I'm on python 2.7.12 using package version 0.5.1.

Example the triggers the problem:

choices = [u'Some Company – Location']
build_choose_clause(choices, u'DisplayName')

python2.7/site-packages/quickbooks/utils.pyc in build_choose_clause(choices, field)
     27         for choice in choices:
     28             if isinstance(choice, six.string_types):
---> 29                 where.append("'{0}'".format(choice.replace("'", "\'")))
     30             else:
     31                 where.append("{0}".format(choice))

UnicodeEncodeError: 'ascii' codec can't encode character u'\u2013' in position 24: ordinal not in range(128)

Attachable API not working

I was trying to upload a PDF attachment to a Bill, and got the following error..

File "/opt/SERVER_NAME/libraries/quickbooksV0_6_1/objects/attachable.py", line 61, in save
json_data = qb.create_object(self.qbo_object_name, self.to_json(), _file_path=self._FilePath)
File "/opt/SERVER_NAME/libraries/quickbooksV0_6_1/client.py", line 337, in create_object
results = self.make_request("POST", url, request_body, file_path=_file_path)
File "/opt/SERVER_NAME/libraries/quickbooksV0_6_1/client.py", line 290, in make_request
headers=headers, params=params, data=request_body)
File "/usr/local/lib/python2.7/dist-packages/rauth/session.py", line 210, in request
return super(OAuth1Session, self).request(method, url, **req_kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 502, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 612, in send
r = adapter.send(request, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 440, in send
timeout=timeout
File "/usr/local/lib/python2.7/dist-packages/urllib3/connectionpool.py", line 601, in urlopen
chunked=chunked)
File "/usr/local/lib/python2.7/dist-packages/urllib3/connectionpool.py", line 357, in _make_request
conn.request(method, url, **httplib_request_kw)
File "/usr/lib/python2.7/httplib.py", line 1017, in request
self._send_request(method, url, body, headers)
File "/usr/lib/python2.7/httplib.py", line 1051, in _send_request
self.endheaders(body)
File "/usr/lib/python2.7/httplib.py", line 1013, in endheaders
self._send_output(message_body)
File "/usr/lib/python2.7/httplib.py", line 868, in _send_output
self.send(message_body)
File "/usr/lib/python2.7/httplib.py", line 840, in send
self.sock.sendall(data)
File "/usr/local/lib/python2.7/dist-packages/urllib3/contrib/pyopenssl.py", line 316, in sendall
sent = self._send_until_done(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE])
File "/usr/local/lib/python2.7/dist-packages/urllib3/contrib/pyopenssl.py", line 304, in _send_until_done
return self.connection.send(data)
File "/usr/local/lib/python2.7/dist-packages/OpenSSL/SSL.py", line 1540, in send
self._raise_ssl_error(self._ssl, result)
File "/usr/local/lib/python2.7/dist-packages/OpenSSL/SSL.py", line 1456, in _raise_ssl_error
_raise_current_error()
File "/usr/local/lib/python2.7/dist-packages/OpenSSL/_util.py", line 54, in exception_from_error_queue
raise exception_type(errors)
Error: [('SSL routines', 'SSL3_WRITE_PENDING', 'bad write retry')]

This error occurs randomly for different attachments. But it fails for the same attachments only, and on the same server. It works on other server.

The library versions on server are as follows --
Lib - Request --> 2.18.2
Lib - urllib3 --> 1.22

Can you please look into it?

build_choose_clause does not properly escape single quote

I've been receiving several QuickbooksException: Error parsing query recently when trying to query a list of customers. I tracked it down to the build_choose_clause and it looks like it's not properly escaping single quote characters as if I do something like Customer.choose[u"Someone's Company"], field=u'DisplayName', qb=client) then I get the following in response:

python2.7/site-packages/quickbooks/client.pyc in handle_exceptions(self, results)
    316                 raise SevereException(message, code, detail)
    317             else:
--> 318                 raise QuickbooksException(message, code, detail)
    319
    320     def create_object(self, qbbo, request_body, _file_path=None):

QuickbooksException: Error parsing query

I'm using python 2.7.12 and version 0.5.1 of the package. Also it looks like the error detail isn't getting include in the error output but that's a separate issue I think.

I tried passing the string directly to the build_choose_clause function and you can see the quote is not escaped:

choices = [u"Someone's Company"]
build_choose_clause(choices, u'DisplayName') # "DisplayName in ('Someone's Business')"

'TaxRate' has no attribute 'all'

tax_rates = TaxRate.all(qb=client)`

In taxrate.py I see class "TaxRate" inheritate "QuickbooksBaseObject" rather then "QuickbooksManagedObject" which allow all, filter like methods

So, by changing
class TaxRate(QuickbooksTransactionEntity, QuickbooksBaseObject):

to

class TaxRate(QuickbooksTransactionEntity, QuickbooksManagedObject):

will work.

I want to ask is there any reason not using QuickbooksManagedObject in TaxRate

Can I make pull request?

<= and >= Queries

utils.py:

where.append("{0} = '{1}'".format(key, value.replace("'", "\'")))

Does not allow for <= or >= queries.

Unorderable types when saving

When updating a Customer using the same code from tests I am getting an unorderable types error (traceback below). If I print the id's and type(customer.id) I'm getting:
2 <class 'str'>

I'm getting the same error when I test the account object as well.

If this needs updating I am happy to submit a pull request.

Traceback (most recent call last): File "<console>", line 1, in <module> File ".../python3.5/site-packages/quickbooks/mixins.py", line 71, in save if self.Id and self.Id > 0: TypeError: unorderable types: str() > int()

Documentation

Is there any better documentation on the usage of this library? Tried to create an Invoice with Line Items and it was a struggle.

Updating purchase

I have a purchase object that I would like to modify and save. The purchase object contains a Line attribute (PurchaseLine) which in turn contains an AccountBasedExpenseLineDetail attribute. The AccountBasedExpenseLineDetail is a Ref object, which means that it contains a few attributes by default (value, name, type).

When saving the purchase object, I get an error messages that indicates quickbooks doesn't recognize some of the attributes given. To work around this issue, I can delete these extra attributes before saving the purchase object:

for line in purchase.Line:
    del line.AccountBasedExpenseLineDetail.name
    del line.AccountBasedExpenseLineDetail.value
    del line.AccountBasedExpenseLineDetail.type
purchase.save(qb=client)

Error when creating a new vendor

I am trying to create a new vendor. Here is a code snippet

vendor = Vendor()
vendor.CompanyName = "Test Name"
try:
    vendor.save(qb=client)
except QuickbooksException as e:
   print(e.detail)

The following is printed:

Property Name:Can not instanti specified is unsupported or invalid

However the Vendor object has no property Name. What am I doing wrong?

Error message from example code

I'm getting an error message using the example code:

Traceback (most recent call last):
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/flask/app.py", line 1994, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/alexander/Documents/Programming/QBTest/app.py", line 41, in access_qb
    client.get_access_tokens(request.args['oauth_verifier'])
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/quickbooks/client.py", line 202, in get_access_tokens
    data={'oauth_verifier': oauth_verifier})
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/rauth/service.py", line 359, in get_auth_session
    **kwargs)
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/rauth/service.py", line 332, in get_access_token
    process_token_request(r, decoder, key_token, key_token_secret)
  File "/home/alexander/anaconda3/lib/python3.5/site-packages/rauth/service.py", line 24, in process_token_request
    raise KeyError(PROCESS_TOKEN_ERROR.format(key=bad_key, raw=r.content))
KeyError: "Decoder failed to handle oauth_token with data as returned by provider. A different decoder may be needed. Provider returned: b'oauth_problem=parameter_absent&oauth_parameters_absent=oauth_token'"

The code I am using is below (I'm using Flask):

from flask import Flask, render_template, request
from quickbooks import QuickBooks

app = Flask(__name__)
QUICKBOOKS_CLIENT_KEY = "..my client key..."
QUICKBOOKS_CLIENT_SECRET = "...my client secret..."
CALLBACK_URL = 'http://localhost:5000/qbauth'
authorize_url, request_token, request_token_secret = None, None, None

@app.route('/')
def start_qb():
	print("START QB")
	client = QuickBooks(
		sandbox=True,
		consumer_key= QUICKBOOKS_CLIENT_KEY,
		consumer_secret=QUICKBOOKS_CLIENT_SECRET,
		callback_url = CALLBACK_URL
	)

	authorize_url = client.get_authorize_url()
	request_token = client.request_token
	request_token_secret = client.request_token_secret
	print("REQUEST TOKENS: {} {} {}".format(authorize_url, request_token, request_token_secret))
	return authorize_url

@app.route('/qbauth')
def access_qb():
	print("QB AUTH")
	client = QuickBooks(
		sandbox=True,
		consumer_key=QUICKBOOKS_CLIENT_KEY,
		consumer_secret=QUICKBOOKS_CLIENT_SECRET
	)

	client.authorize_url = authorize_url
	client.request_token = request_token
	client.request_token_secret = request_token_secret
	client.set_up_service()
	print("SET UP SERVICE")
	print(request.args)
	client.get_access_tokens(request.args['oauth_verifier'])

	realm_id = request.args['realmId']
	access_token = client.access_token
	access_token_secret = client.access_token_secret
	print(realm_id, access_token, access_token_secret)
	return "Hello World"

if __name__ == "__main__":
	app.run(debug = True, reloader_type='stat')

Can't load PDF after creating PDF File attachable on Bill

Hi,
I am using the following code:
# Attach PDF Invoice
attachable = Attachable()
attachable.AttachableRef = [{'EntityRef': {'type': 'Bill', 'value': bill.Id}}]
attachable.FileName = 'InvoiceTemplate.pdf'
attachable._FilePath = os.path.dirname(os.path.realpath(file)) + '/InvoiceTemplate.pdf'
attachable.ContentType = 'application/pdf'
attachable.save(qb=self._client)

I get no errors and can see the attachable on the Bill in my sandbox, but when I try to view it I get 'Failed to load PDF document'. If I attach the pdf file in Quickbooks online in my sandbox I can view or download the attached PDF just fine. The attachment created through python shows a size of 372.5kb, and the one created through Quickbooks online shows a size of 215.2kb.

Is this a bug or am I missing a step?
Thanks!

Unable to download Invoice PDF

Hey guys,

So I realized that I am unable to download a Invoice PDF because in the QuickbooksPdfDownloadable object its method download_pdf sets its own Quickbooks object which doesn't contain the correct authorization vars. Is there anyway you could fix this fairly soonish?

Thanks!

Help Getting Oauth to work

I have tried to play around with the python-quickbooks lib, but I can figure out how to get django work. Everytime, I run the script, I get this error.

"oauth_token = request.GET['oauth_token']"
NameError: name 'request' is not defined

I read the documentation, request is from django http.request.
I have my django 1.10 library installed, but just cant figure out how to import lib into the quickbooks script.

I tried this, and it is not working.

from django.http import *
from django.utils.html import *
from django.utils.http import *

Are there a more detail guideline for this?

Bug in to_ref method of Bill

This is the current code of to_ref method:

def to_ref(self):
        ref = Ref()

        ref.name = self.DisplayName
        ref.type = self.qbo_object_name
        ref.value = self.Id

        return ref

Bill objects do not have a property DisplayName.

Throttle Limits Exceeded Error

When I am sending data from my web application to QuickBooks Online using PHP-SDK-V3 and I am getting below errors, can anyone face this error if yes so please help me on this how ca i overcome this issue.

`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse time="2017-07-30T22:45:22.933-07:00" xmlns="http://schema.intuit.com/finance/v3">
    <Fault type="SERVICE">
        <Error code="3001">
            <Message>message=ThrottleExceeded; errorCode=003001; statusCode=429</Message>
            <Detail>The request limit was reached.  Try again later</Detail>
        </Error>
    </Fault>
</IntuitResponse>` 

Issue with oAuth and tokens

So I am trying application like that:

from quickbooks import QuickBooks

clientkey = "xx"
clientsecret = "xx"

client = QuickBooks(
    sandbox=True,
    consumer_key=clientkey,
    consumer_secret=clientsecret,
    callback_url='http://localhost/qbo_token.php'
)

authorize_url = client.get_authorize_url()
request_token = client.request_token
request_token_secret = client.request_token_secret

print(authorize_url,request_token,request_token_secret)

It sends:

('https://appcenter.intuit.com/Connect/Begin?oauth_token=true', u'true', u't6mwhA1AgLFNKjvIOg829Xj6ZycWo2KRKkOtoTre')

Which is a wrong URL.

Purchase order: Property Name:Unrecognized field "BillableSt specified is unsupported or invalid

Hi,
First of all thanks for this beautiful repo, it helped me win so much time on my qb integration.

Im trying right now to create purchase order, but after putting (I think) every correctly, I end up on this error : "2010: Property Name: Unrecognized field "BillableSt specified is unsupported or invalid".
I tryed to remove line 51 or purchaseorder.py:PurchaseOrderLine : ' self.BillableStatus = "" ', seem to solve this problem.

But after doing so, I got an other problem, more strange and can't find a way to do it. I tried to take exemple from the test but don't work.

2010: Property Name:Can not deserialize instance of com.intuit.schema.finance.v3.LineDetailTypeEnum out of START_OBJECT token specified is unsupported or invalid

I'm dropping my code right here, tell me what you think about it, if its on my side (probably) or yours.
Thanks a lot ! :)

    po = PurchaseOrder()

    vendor_obj = Ref()
    vendor_obj.value = "21"
    vendor_obj.name = "Cali'Remy_Remy_Alves"
    po.VendorRef = vendor_obj

    expenseAccount = Ref()
    expenseAccount.name = "Cost of Goods Sold"
    expenseAccount.value = "60"
    po.APAccountRef = expenseAccount

    line = PurchaseOrderLine()
    line.LineNum = 1
    line.Amount = 42
    line.Description = "Yolo description"

    line.DetailType = ItemBasedExpenseLineDetail()
    item = Item.all(max_results=1, qb=client)[0]
    line.DetailType.ItemRef = item.to_ref()

    po.Line.append( line )

    po.save(qb=client)

Have a good day

Add Oauth2 support.

If I want add changes for support Oauth2 (my current project need this), must i create new module for ouath2 implementation?
Oauth1 realised in client.py and maybe right decision be puting it implementations out in two modules oauth1c and oauth2c ?

In client `get_authorize_url` method, `request_token` and `request_token_secret` are set to lists when they should be strings

Hi there! Great work on this library.

I believe I have found a bug. I configured the QuickBooks client class and ran get_authorize_url(), which returned a URL that didn't work. I decoded the URL and saw that the oauth_token value was wrapped in a list:

https://appcenter.intuit.com/Connect/Begin?oauth_token=[u'qyprdwHrErQQ3Lxj6NeTbtcmhLvL1KeSsphQgWmmn3IgFev']

I removed the brackets, tried the URL again, and was able to authorize my QBO app. I inspected quickbooks.request_token and quickbooks.request_token_secret in the console, and saw that they were also wrapped in lists. Manually unwrapping them from the lists allowed me to continue connecting to the API without issue.

I believe the issue is in get_authorize_url where the request_token and request_token_secret are assigned. They seem to be expecting strings, but are instead getting strings wrapped in lists from oauth_resp:

def get_authorize_url(self):
    ...
    response = self.qbService.get_raw_request_token(
           params={'oauth_callback': self.callback_url})

    oauth_resp = parse_qs(response.text)

    self.request_token = oauth_resp['oauth_token']
    self.request_token_secret = oauth_resp['oauth_token_secret']

    return self.qbService.get_authorize_url(self.request_token)

I printed oauth_resp for debugging purposes.

{
    u'oauth_token_secret': [u'WzU87wYNheeDq3lLQJuFMHqPfeyEcJU9dMZQ606w'],
    u'oauth_token': [u'qyprdxWmp4ZbBMZCuiaUXVXTrMDGzFPBRmz9yQLT1NdMLtCz'],
    u'oauth_callback_confirmed': [u'true']
}

It's confusing, because I wouldn't expect any of those values to be lists. I wonder if this perhaps an issue/peculiarity of the rauth library? For now, I will probably hack a quick fix and just grab the 0 index from each to make it work.

What are your thoughts?

Thanks.

Count not working

Hi, i'm trying to do Customer.query("SELECT COUNT(*)FROM Customer", qb=client) but it's just returning an empty list.

Add support for downloading PDFs

First of all, thank you for the great work, you've done so far with this library!

The quickbooks API allows downloading PDF for some of the "TRANSACTION ENTITIES":

  • Estimate
  • Invoice
  • SalesReceipt

I'm going to need this functionality, so I'll probably implement this. Any advice on where to place the code? Should I create a new class (say QuickbooksPdfDownloadable?) that will be extended by the listed classes and will require implementation of get_pdf_api_url() abstract method or something like that?

What should the tests look like? Are they required?

Broken support for Item Categories

Activated Item Categories in my sandbox, yet the library seems to not be able to handle it.

  1. Item.all(..) will return the Categories as well, but the Type = 'Service'
  2. Item.where("Type = 'Category'",..) is behaving like .all()
  3. Item.query("SELECT * FROM Item WHERE Type = 'Category'",..) same behavior.

For the record: I am passing in minorversion=4 to the client.

FYI: https://developer.intuit.com/v2/apiexplorer?apiname=V3QBO#?id=Item Seems to behave properly.

EDIT: My bad .. I had minor_version=4 ..
Areas for improvement:

  1. PEP consistency on the argument name.. should be minor_version ;-)
  2. Quickbooks.new should throw KeyError if invalid argument name passed in. (Debatable)

SSLError: [Errno 1] _ssl.c:510: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

I'm getting the following error when calling QuickBooks(**kwargs).get_authorize_url() method.

Traceback is

>>> qb_object.get_authorize_url()

C:\Users\t0429\projenv\lib\site-packages\requests\packages\urllib3\util\ssl_.py:334: SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an i
ncorrect TLS certificate, which can cause validation failures. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  SNIMissingWarning

C:\Users\t0429\projenv\lib\site-packages\requests\packages\urllib3\util\ssl_.py:132: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can up
grade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecurePlatformWarning

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Users\t0429\projenv\lib\site-packages\quickbooks\client.py", line 159, in get_authorize_url
    params={'oauth_callback': self.callback_url})
  File "C:\Users\t0429\projenv\lib\site-packages\rauth\service.py", line 215, in get_raw_request_token
    **kwargs)
  File "C:\Users\t0429\projenv\lib\site-packages\rauth\session.py", line 210, in request
    return super(OAuth1Session, self).request(method, url, **req_kwargs)
  File "C:\Users\t0429\projenv\lib\site-packages\requests\sessions.py", line 488, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Users\t0429\projenv\ib\site-packages\requests\sessions.py", line 609, in send
    r = adapter.send(request, **kwargs)
  File "C:\Users\t0429\projenv\lib\site-packages\requests\adapters.py", line 497, in send
    raise SSLError(e, request=request)
SSLError: [Errno 1] _ssl.c:510: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

Any hint what am I missing?

New OAuth2 protocal token issue--absence of access_token_secret

It appears the new OAuth2 protocol returns only access_token and refresh_token. Thus, I am unable to initialize the quickbooks-python client with a access_token_secret.

For new apps, it appears the code will need to be updated for the new requirements effective 7-17-2017.

The new OAuth2 protocol, effective for new apps, is discussed here:
https://developer.intuit.com/docs/0100_quickbooks_online/0100_essentials/000500_authentication_and_authorization/connect_from_within_your_app

Renew Tokens

Hi,

I'm new with using the quickbooks API. After 180 days the token has expired. How do I renew the quickbooks token?

Moses

ValidationFault for Invoices

Issue:
QuickBooks returns a ValidationFault error when POSTing Invoices.

Sample Response:
{u'type': u'ValidationFault', u'Error': [{u'Message': u'Request has invalid or unsupported property', u'code': u'2010', u'Detail': u'Property Name:Can not instanti specified is unsupported or invalid'}]}

Information on this error here

Sample code that generates this error:
invoice = Invoice()
invoice.CustomerRef = unicode(61)
invoice.save()

Analysis:
It seems that the package is either failing to instantiate the Invoice object itself, or is mistakenly sending an additional 'field' called Can not instanti with its POST call. I think that the former is most likely, but I haven't been able to nail down where the error is occurring. I've tried setting the CustomerRef value to:

  • A simple integer
  • Unicode of integer
  • An actual Customer object

but I get the same behavior for each approach. Removing the CustomerRef and attempting to POST an Invoice with no other attributes causes QuickBooks to return a Required param missing error. I am successfully able to POST and GET Customer objects, so I do not believe that this is an issue with credentials, environment settings, etc. Any help would be greatly appreciated - it is entirely possible that I am simply misusing the Invoice object in a way that I can't identify.

Typo in README

from quickbooks.object.customer import Customer should be from quickbooks.objects.customer import Customer

RefundReceipt is not a valid QBO Business Object

Hello,

Is RefundReceipt functional? I'm trying to create a simple RefundReceipt object and receiving the following traceback:

Traceback (most recent call last): File "main.py", line 141, in <module> r.save() File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/quickbooks/mixins.py", line 66, in save json_data = qb.create_object(self.qbo_object_name, self.to_json()) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/quickbooks/client.py", line 215, in create_object self.isvalid_object_name(qbbo) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/quickbooks/client.py", line 230, in isvalid_object_name raise Exception("{0} is not a valid QBO Business Object.".format(object_name)) Exception: RefundReceipt is not a valid QBO Business Object.

Doing the same with SalesReceipt is fully functional. Thank you!

IDEA: What exactly does enable_global() do?

I recommend you explain the QuickBooks.enable_global() (as well as disable_globals) in more detail on the README.

I went in the code to see that these toggle a singleton pattern but didn't get that from the README which states "If your consumer_key never changes you can enable the client to stay running" but actually what if I have a single application but several company's I want to open multiple API client connections with? My consumer key never changes but I still don't want singleton.

Just a thought!

Access Token is empty

After successful OAuth2 verification, in the callback, I have used the Oauth2SessionManager instance to create the client.

get_access_tokens method does throw a

/opt/boxen/data/virturalenvs/rbox/lib/python2.7/site-packages/urllib3/util/ssl_.py:137: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecurePlatformWarning

The client object does not have an access token.

I use Python 2.7

.all() method only returns 100 rows

I believe QBO requires paging for large results sets. So Customer.all() returns only the first 100.

(More correctly, this issue should be titled: "query() method only processes the first page of results")

Easier Objects Import

Importing objects right now requires to know the filenames inside of the objects directory. Instead, I propose importing all the relevant objects into the objects/__init__.py file so that objects can be imported like this:

from quickbooks.objects import Customer, Invoice

Let me know what you guys think. If you'd like, I can submit a PR for this change.

Delete Objects

I can't seem to find a way to delete an object. Is there with the existing API? If not I can probably submit a PR to do so. Was thinking of creating a DeleteMixin.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.