2015-06-02 3 views
1

Мне нужна помощь в реализации приложения python, которое обращается к API Quickbooks. Я успешно написал несколько приложений, которые используют API, но как только мы попадаем в мир OAuth, я немного теряюсь.python с Quickbooks Online API v3

Во всяком случае, я нашел QuickBooks-питона обертку здесь: https://github.com/troolee/quickbooks-python

но есть нулевые примеры рабочего кода, показывающие, как правильно реализовать. Я полагаю, что более опытный программист на питоне мог бы выяснить, как сделать эту работу без каких-либо инструкций, но мне кажется, что я пропускаю основы.

Если бы я мог получить это связано, я мог бы заставить его работать оттуда ...

Похоже документации на GitHub прыгает вокруг и для более опытных программистов, вероятно, сделать совершенный смысл. Но я просто не после ...

from quickbooks import * 

consumerKey =   "fromApiConsole" 
consumerSecret =  "fromApiConsole" 
callbackUrl =   "https://quickbooks.api.intuit.com/v3" 

qbObject = QuickBooks(
     consumer_key = consumerKey, 
     consumer_secret = consumerSecret, 
     callback_url = callbackUrl 
     ) 

authorize_url = qbObject.get_authorize_url() # will create a service, and further set up the qbObject. 

oauth_token = request.GET['oauth_token'] 
oauth_verifier = request.GET['oauth_verifier'] 
realm_id = request.GET['realmId'] 

session = qbObject.get_access_tokens(oauth_verifier) 

# say you want access to the reports 

reportType = "ProfitAndLoss" 

url = "https://quickbooks.api.intuit.com/v3/company/asdfasdfas/" 
url += "reports/%s" % reportType 

r = session.request(#This is just a Rauth request 
    "POST", 
    url, 
    header_auth = True, 
    realm = realm_id, 
    params={"format":"json"} 
    ) 

qb = QuickBooks(
    consumer_key = consumerKey, 
    consumer_secret = consumerSecret, 
    access_token = qbtoken.access_token, # the stored token 
    access_token_secret = qbtoken.access_token_secret, # the stored secret 
    company_id = qbtoken.realm_id #the stored realm_id 
    ) 

qbText = str(qb.query_objects(business_object, params, query_tail)) 

print qbText 

Я уверен, что я:

  1. импортирования неправильных модулей/классов
  2. отсутствующих огромных кусков кода, чтобы «склеить» образцы, найденные на GitHub
  3. не используя Джанго здесь и я знаю, класс запроса выше в Джанго, но я бы очень хотел, чтобы просто сделать эту работу как питон скрипт без использования Джанго
  4. не получает маркер/язь ntifier/realmId из начальной функции authorize_url. он печатает на экране, но я не уверен, как захватить его ...

Конечная цель здесь действительно просто подключить и получить L заявление P & от Quickbooks Online. Если я смогу зайти так далеко, я уверен, что смогу получить остальную часть того, что мне нужно, от API. Мне действительно не нужно ИЗМЕНИТЬ ничего, я просто хочу включить данные из отчетов в некоторые информационные панели.

* UPDATE *

хорошо, я понял, как заставить его подключить, но я не уверен, как добраться до отчетов.

ответ был этим, который был на предыдущей странице API:

Accessing the API 
Once you've gotten a hold of your QuickBooks access tokens, you can create a QB object: 

qb = QuickBooks(consumer_key = QB_OAUTH_CONSUMER_KEY, 
     consumer_secret = QB_OAUTH_CONSUMER_SECRET, 
     access_token = QB_ACCESS_TOKEN, 
     access_token_secret = QB_ACCESS_TOKEN_SECRET, 
     company_id = QB_REALM_ID 
     ) 

до сих пор пытаюсь получить базовые отчеты ...

ответ

2

Итак, вот как сделать эта работа. Я сосредоточен на отчетах, так вот, как вы можете получить отчеты Quickbooks Online API с помощью Python:

1) Перейти к https://github.com/finoptimal-dev/quickbooks-python и скачать код

2) Убедитесь, что вы установили Rauth. Если вы находитесь на AWS/EC2, просто:

sudo yum install rauth 

3) Отредактируйте файл quickbooks2.py и добавьте следующие строки в конце:

qb = QuickBooks(consumer_key = QB_OAUTH_CONSUMER_KEY, 
     consumer_secret = QB_OAUTH_CONSUMER_SECRET, 
     access_token = QB_ACCESS_TOKEN, 
     access_token_secret = QB_ACCESS_TOKEN_SECRET, 
     company_id = QB_REALM_ID 
     ) 

4) Установка приложения песочница на сайте Quickbooks здесь: https://developer.intuit.com/v2/ui#/app/startcreate (вам нужно будет создать учетную запись разработчика, если у вас ее еще нет)

5) После настройки вы можете перейти на вкладку «Ключи» приложения и захватить App Token, OAuth Consumer Секрет секретности ключа и OAuth.

6) Перейдите на площадку разработчика Intuit по адресу https://appcenter.intuit.com/Playground/OAuth/IA и используйте информацию с шага 5, чтобы получить токен доступа и секретный токен доступа.

7) Измените переменные на шаге 3 на правильные значения. Для QB_REALM_ID это идентификатор компании. Вы можете получить это в песочнице, войдя в https://developer.intuit.com/v2/ui#/sandbox и ищите Идентификатор компании.

7) добавить следующий код, приведенный ниже код из шага # 3 выше

print qb.get_report('ProfitAndLoss','summarize_column_by=Month&start_date=2014-01-01&end_date=2014-12-31') 

Я использую вышеуказанные даты б/с компании Quickbooks песочнице не имеет данных доходов/расходов в 2015 году, так что вы должны выберите даты в 2014 году.

8) ВАЖНО: Для использования в песочнице Quickbooks для целей отчетности вам необходимо изменить функцию get_report(), чтобы использовать base_url_v3 вместо жесткого кодирования для производственного URL.

Посмотрите на строку в функции Get_Report(), которая выглядит следующим образом:

url = "https://quickbooks.api.intuit.com/v3/company/%s/" % \ 

и изменить его к этому:

url = self.base_url_v3 + "/company/%s/" % \ 

9) Теперь вы можете изменить base_url_v3 весь путь в верх к этому:

base_url_v3 = "https://sandbox-quickbooks.api.intuit.com/v3" 

10) И теперь вы должны теперь быть в состоянии выполнить:

python quickbooks2.py 

Вы должны увидеть кучу данных JSON от компании Quickbooks Sandbox.

11) Вы можете исследовать немного, чтобы проверить соответствующие URL-адреса здесь: https://developer.intuit.com/apiexplorer?apiname=V3QBO#Reports

12) Ссылка отчет здесь: и это показывает, какие параметры вы можете использовать. Чтобы проверить параметры в проводнике, вы вводите их в разделе «Тело запроса».

Я боролся с этим некоторое время и, наконец, понял это. Надеюсь, это помогает кому-то другому.

+0

> получить токен доступа и секретный токен доступа. Проблема в том, что токен истекает через день, не так ли? Я хочу access_token, который не истекает, поскольку я сам буду использовать приложение. – Volatil3

0

У меня нет большого опыта работы с Python, но кто-то делило этот код со мной для oauth ранее. Если у вас есть дополнительные вопросы по коду, я не смогу ответить на них.

ПРИМЕЧАНИЕ. Приведенный ниже код также вызывает вызовы для V2 QBO apis. Пожалуйста, не используйте эту часть, поскольку она устарела.

Смотрите, если он helps-

Импорт Python

из Rauth импорта OAuth1Session, OAuth1Service импорта xml.etree.ElementTree как ET

импорт xmltodict

QuickBooks класса(): "" "Класс-оболочка вокруг модуля Пайтона Rauth для Quickbooks АНИ" ""

access_token = '' 
access_token_secret = '' 
consumer_key = '' 
consumer_secret = '' 
company_id = 0 
callback_url = '' 
session = None 

base_url_v3 = "https://quickbooks.api.intuit.com/v3" 
base_url_v2 = "https://qbo.intuit.com/qbo1" 

request_token_url = "https://oauth.intuit.com/oauth/v1/get_request_token" 
access_token_url = "https://oauth.intuit.com/oauth/v1/get_access_token" 

authorize_url = "https://appcenter.intuit.com/Connect/Begin" 

# Things needed for authentication 
qbService = None 

request_token = '' 
request_token_secret = '' 


def __init__(self, **args): 

    if 'consumer_key' in args: 
     self.consumer_key = args['consumer_key'] 

    if 'consumer_secret' in args: 
     self.consumer_secret = args['consumer_secret'] 

    if 'access_token' in args: 
     self.access_token = args['access_token'] 

    if 'access_token_secret' in args: 
     self.access_token_secret = args['access_token_secret'] 

    if 'company_id' in args: 
     self.company_id = args['company_id'] 

    if 'callback_url' in args: 
     self.callback_url = args['callback_url'] 


def get_authorize_url(self): 
    """Returns the Authorize URL as returned by QB, 
    and specified by OAuth 1.0a. 
    :return URI: 
    """ 
    self.qbService = OAuth1Service(
      name = None, 
      consumer_key = self.consumer_key, 
      consumer_secret = self.consumer_secret, 
      request_token_url = self.request_token_url, 
      access_token_url = self.access_token_url, 
      authorize_url = self.authorize_url, 
      base_url = None 
     ) 
    self.request_token, self.request_token_secret = self.qbService.get_request_token(
      params={'oauth_callback':self.callback_url} 
     ) 

    return self.qbService.get_authorize_url(self.request_token) 




def get_access_tokens(self, oauth_verifier): 
    """Wrapper around get_auth_session, returns session, and sets 
    access_token and access_token_secret on the QB Object. 
    :param oauth_verifier: the oauth_verifier as specified by OAuth 1.0a 
    """ 
    session = self.qbService.get_auth_session(
      self.request_token, 
      self.request_token_secret, 
      data={'oauth_verifier': oauth_verifier}) 

    self.access_token = session.access_token 
    self.access_token_secret = session.access_token_secret 

    return session 



def create_session(self): 
    if self.consumer_secret and self.consumer_key and self.access_token_secret and self.access_token: 
     # print "hi" 
     session = OAuth1Session(self.consumer_key, 
      self.consumer_secret, 
      self.access_token, 
      self.access_token_secret, 
      ) 
     # print session 
     self.session = session 
    else: 
     pass 
     #TODO: raise an error 
    return self.session 


def keep_trying(self, r_type, url, header_auth, realm, payload=''): 

    if self.session != None: 
     session = self.session 
    else: 
     session = self.create_session() 
     self.session = session 

    trying = True 
    tries = 0 
    while trying: 
     print url 
     tries += 1 
     if "v2" in url: 
      r = session.request(r_type, url, header_auth, realm, data=payload) 

      r_dict = xmltodict.parse(r.text) 
      # print "DICT", r_dict 
      if "FaultInfo" not in r_dict or tries > 4: 
       trying = False 
     else: 
      # url = "https://qb.sbfinance.intuit.com/v3/company/184010684/query?query=SELECT * FROM JournalEntry" 
      # url = "https://quickbooks.api.intuit.com/v3/company/184010684/journalentry/24772" 
      # url = "https://quickbooks.api.intuit.com/v3/company/184010684/query?query='SELECT+*+FROM+JournalEntry'" 
      # url = "https://qb.sbfinance.intuit.com/v3/company/184010684/query?query=SELECT%20%2A%20FROM%20JournalEntry&" 
      print url, r_type 
      headers = {'Accept': 'application/json'} 
      r = session.request(r_type, url, header_auth, realm, headers = headers) 
      # r.headers 
      print "\n\n INITIAL TEXT \n\n", r.text 


      print "request headers:", r.request.headers 
      print "request URL:", r.request.url 
      print "response headers:", r.headers 

      r_dict = r.text 
      if "Fault" not in r_dict or tries > 4: 
       trying = False 
      r_dict = [] 

    return r_dict 

def fetch_customer(self, pk): 

    if pk: 
     url = self.base_url_v2 + "/resource/customer/v2/%s/%s" % (self.company_id, pk) 
     r_dict = self.keep_trying("GET", url, True, self.company_id) 

     return r_dict['Customer'] 


def fetch_customers(self, all=False, page_num=0, limit=10): 
    if self.session != None: 
     session = self.session 
    else: 
     session = self.create_session() 
     self.session = session 

    # We use v2 of the API, because what the fuck, v3. 
    url = self.base_url_v2 
    url += "/resource/customers/v2/%s" % (self.company_id) 

    customers = [] 

    if all: 
     counter = 1 
     more = True 

     while more: 
      payload = { 
       "ResultsPerPage":30, 
       "PageNum":counter, 
       } 

      trying = True 

      # Because the QB API is so iffy, let's try until we get an non-error 

      # Rewrite this to use same code as above. 
      while trying: 
       r = session.request("POST", url, header_auth = True, data = payload, realm = self.company_id) 
       root = ET.fromstring(r.text) 
       if root[1].tag != "{http://www.intuit.com/sb/cdm/baseexceptionmodel/xsd}ErrorCode": 
        trying = False 
       else: 
        print "Failed" 

      session.close() 
      qb_name = "{http://www.intuit.com/sb/cdm/v2}" 

      for child in root: 
       # print child.tag, child.text 
       if child.tag == "{http://www.intuit.com/sb/cdm/qbo}Count": 

        if int(child.text) < 30: 
         more = False 
         print "Found all customers" 

       if child.tag == "{http://www.intuit.com/sb/cdm/qbo}CdmCollections": 
        for customer in child: 

         customers += [xmltodict.parse(ET.tostring(customer))] 

      counter += 1 

      # more = False 
      # print more 

    else: 

     payload = { 
      "ResultsPerPage":str(limit), 
      "PageNum":str(page_num), 
      } 

     r = session.request("POST", url, header_auth = True, data = payload, realm = self.company_id) 

     root = ET.fromstring(r.text) 

     #TODO: parse for all customers 


    return customers 

def fetch_sales_term(self, pk): 
    if pk: 
     url = self.base_url_v2 + "/resource/sales-term/v2/%s/%s" % (self.company_id, pk) 
     r_dict = self.keep_trying("GET", url, True, self.company_id) 
     return r_dict 


def fetch_invoices(self, **args): 
    if "query" in args: 
     payload = "" 
     if "customer" in args['query']: 
      payload = { 
       "Filter":"CustomerId :Equals: %s" % (args['query']['customer']) 
      } 


     # while more: 
     url = self.base_url_v2 + "/resource/invoices/v2/%s/" % (self.company_id) 
     r_dict = self.keep_trying("POST", url, True, self.company_id, payload) 
     invoices = r_dict['qbo:SearchResults']['qbo:CdmCollections']['Invoice'] 

     return invoices 
    elif "pk" in args: 
     # TODO: Not tested 
     url = self.base_url_v2 + "/resource/invoice/v2/%s/%s" % (self.company_id, args['pk']) 
     r_dict = self.keep_trying("GET", url, True, self.company_id) 
     return r_dict 
    else: 
     url = self.base_url_v2 + "/resource/invoices/v2/%s/" % (self.company_id) 
     r_dict = self.keep_trying("POST", url, True, self.company_id, payload) 
     return "BLAH" 


def fetch_journal_entries(self, **args): 
    """ Because of the beautiful way that journal entries are organized 
    with QB, you're still going to have to filter these results for the 
    actual entity you're interested in. Luckily it only returns the entries 
    that are relevant to your search 

    :param query: a dictionary that includes 'customer', and the QB id of the 
     customer 
    """ 

    if "query" in args: 
     payload = {} 
     more = True 
     counter = 1 
     journal_entries = [] 

     if "customer" in args['query']: 

      payload = { 
       "Filter":"CustomerId :Equals: %s" % (args['query']['customer']) 
      } 

      # payload = { 
      #  "query":"SELECT * FROM JournalEntry", 
      # } 

     while more: 

      payload["ResultsPerPage"] = 30 

      payload["PageNum"] = counter 

      # url = self.base_url_v2 + "/resource/journal-entries/v2/%s/" % (self.company_id) 
      # url = self.base_url_v3 + "/company/%s/query" % (self.company_id) 
      url = "https://qb.sbfinance.intuit.com/v3/company/184010684/query?query=SELECT%20%2A%20FROM%20JournalEntry&" 

      r_dict = self.keep_trying("GET", url, True, self.company_id, payload) 

      more = False 
      # print r_dict['qbo:SearchResults']['qbo:Count'] 
      counter = counter + 1 
      # if int(r_dict['qbo:SearchResults']['qbo:Count']) < 30: 
       # more = False 

      # journal_entry_set = r_dict['qbo:SearchResults']['qbo:CdmCollections']['JournalEntry'] 
      # journal_entries += [journal_entry_set] 
     return [] 
     # return r_dict['qbo:SearchResults']['qbo:CdmCollections']['JournalEntry'] 

    elif "pk" in args: 
     # TODO: Not Tested 
     url = self.base_url_v2 + "/resource/journal-entry/v2/%s/%s" % (self.company_id, args['pk']) 
     r_dict = self.keep_trying("GET", url, True, self.company_id) 
     return r_dict 
    else: 
     url = self.base_url_v2 + "/resource/journal-entries/v2/%s/" % (self.company_id) 
     r_dict = self.keep_trying("POST", url, True, self.company_id) 
     print r_dict 
     return "BLAH" 
+0

спасибо nimisha, но я все еще не понимаю, как использовать этот код. я не могу заставить его «вообще» что-либо делать. инструкции на странице github на самом деле не говорят вам, как ее использовать. Даже если вы вернетесь к версии simonv3, он просто скажет: «Работает как любой модуль Python, но вам понадобится рана». – gotmike

Смежные вопросы