Code Monkey home page Code Monkey logo

Exact Online REST API Library in Python

Exact Online provides accounting software in a Software-as-a-Service delivery model. It implements an API through a REST interface. This library aims to ease its use.

Quick jump

Usage by example

Set up the basics:

from exactonline.api import ExactApi
from exactonline.exceptions import ObjectDoesNotExist
from exactonline.storage import IniStorage

# Create a function to get the api with your own storage backend.
def get_api():
    storage = IniStorage('/path/to/config.ini')
    return ExactApi(storage=storage)
api = get_api()

Get an invoice:

# Get an invoice by your own invoice number (YourRef).
# Returns a dictionary, or raises ObjectDoesNotExist.
invoice = api.invoices.get(invoice_number='F0005555')

It looks somewhat like this:

invoice == {
    u'AmountDC': 50.8,
    u'AmountFC': 50.8,
# ...
    u'SalesEntryLines': [
        {u'AmountDC': 41.98,
         u'AmountFC': 41.98,
# ...
         u'Description': u'Omzet backups',
         u'VATBaseAmountDC': 41.98,
         u'VATBaseAmountFC': 41.98},
    ],
# ...
    u'VATAmountDC': 8.82,
    u'VATAmountFC': 8.82,
    u'YourRef': u'F0005555',
    u'__metadata': {u'type': u'Exact.Web.Api.Models.SalesEntry',
                    u'uri': u"https://start.exactonline.nl/api/v1/..."},
}

Get relations:

relations_limit_2 = api.relations.filter(top=2)
# that was cheaper than: api.relations.all()[0:2]

relations_limit_2 == [
    {u'Code': u'              1068',
     u'ID': u'11111111-2222-3333-4444-555555555555',
     u'Name': u'ACME Corporation',
     u'__metadata': {u'type': u'Exact.Web.Api.Models.Account',
                     u'uri': u"https://start.exactonline.nl/api/v1/...')"}},
    {u'Code': u'               555',
     u'ID': u'22222222-3333-4444-5555-666666666666',
     u'Name': u'Daffy Duck Ltd.',
     u'__metadata': {u'type': u'Exact.Web.Api.Models.Account',
                     u'uri': u"https://start.exactonline.nl/api/v1/...')"}}
]

Update a relation:

daffy_duck = api.relations.get(relation_code='555')
api.relations.update(daffy_duck['ID'], {'Name': 'Daffy Duck and sons'})

Delete a relation:

daffy_duck = api.relations.get(relation_code='555')
api.relations.delete(daffy_duck['ID'])

Create an invoice:

customer_data = api.relations.get(relation_code='123')  # local relation_code
customer_guid = customer_data['ID']
invoice_data = {
    'AmountDC': str(amount_with_vat),  # DC = default currency
    'AmountFC': str(amount_with_vat),  # FC = foreign currency
    'EntryDate': invoice_date.strftime('%Y-%m-%dT%H:%M:%SZ'),  # pretend we're in UTC
    'Customer': customer_guid,
    'Description': u'Invoice description',
    'Journal': remote_journal,  # 70 "Verkoopboek"
    'ReportingPeriod': invoice_date.month,
    'ReportingYear': invoice_date.year,
    'SalesEntryLines': [],
    'VATAmountDC': str(vat_amount),
    'VATAmountFC': str(vat_amount),
    'YourRef': local_invoice_number,
    # must start uniquely at the start of a year, defaults to:
    # YYJJ0001 where YY=invoice_date.year, and JJ=remote_journal
    'InvoiceNumber': '%d%d%04d' % (invoice_date.year, remote_journal,
                                   int(local_invoice_number)),
}
# The SalesEntryLines need to be filled with a bunch of dictionaries
# with these keys: AmountDC, AmountFC, Description, GLAccount,
# VATCode where GLAccount holds the Journal remote GUID, and the
# amounts are without VAT.

api.invoices.create(invoice_dict)

You may need to play around a bit to find out which fields are mandatory, and what kind of values the fields need. The Exact Online REST resources list isn't always clear on that.

You'll need a storage backend. The default IniStorage can be taken from exactonline.storage.

from exactonline.storage import IniStorage

class MyIniStorage(IniStorage):
    def get_response_url():
        "Configure your custom response URL."
        return self.get_base_url() + '/oauth/success/'

storage = MyIniStorage('/path/to/config.ini')

(Note that you're not tied to using .ini files. See exactonline/storage.py if you want to use a different storage backend.)

You need to set up access to your Exact Online SaaS instance, by creating an export link. See creating Exact Online credentials for more info.

Take that info, and configure it in your config.ini.

[server]
auth_url = https://start.exactonline.co.uk/api/oauth2/auth
rest_url = https://start.exactonline.co.uk/api
token_url = https://start.exactonline.co.uk/api/oauth2/token

[application]
base_url = https://example.com
client_id = {12345678-abcd-1234-abcd-0123456789ab}
client_secret = ZZZ999xxx000

Create an initial URL:

api = ExactApi(storage=storage)
url = api.create_auth_request_url()

The URL will look like this; redirect the user there so he may authenticate and allow your application access to Exact Online (this is OAuth):

After authentication he will get redirected back to:

You should implement a view on that URL, that does basically this:

api.request_token(code)

At this point, you should configure your default division, if you haven't already:

division_choices, current_division = api.get_divisions()
api.set_division(division_choices[0][0])  # select ID of first division

Now you're all set!

Implemented resources

View exactonline/api/__init__.py to see which resource helpers are implemented.

Currently, it looks like this:

invoices = Invoices.as_property()
ledgeraccounts = LedgerAccounts.as_property()
receivables = Receivables.as_property()
relations = Relations.as_property()

But you can call resources which don't have a helper directly. The following two three are equivalent:

api.relations.all()
api.restv1('GET', 'crm/Accounts')
api.rest('GET', 'v1/%d/crm/Accounts' % selected_division)

As are the following three:

api.relations.filter(top=2)
api.restv1('GET', 'crm/Accounts?$top=2')
api.rest('GET', 'v1/%d/crm/Accounts?$top=2' % selected_division)

And these:

api.invoices.filter(filter="EntryDate gt datetime'2015-01-01'")
api.restv1('GET', 'salesentry/SalesEntries?' +
  '$filter=EntryDate%20gt%20datetime%272015-01-01%27')
api.rest('GET', 'v1/%d/salesentry/SalesEntries?' +
  '$filter=EntryDate%%20gt%%20datetime%%272015-01-01%%27' %
  selected_division)
# convinced yet that the helpers are useful?

See the Exact Online REST resources list for all available resources.

Other benefits

The ExactApi class ensures that:

  • Tokens are refreshed as needed (see: exactonline/api/autorefresh.py).
  • Paginated lists are automatically downloaded in full (see: exactonline/api/unwrap.py).

Creating Exact Online credentials

Previously, one could create an API from the Exact Online interface directly. This was removed at some point between 2014 and 2015.

According to the "how can I create an application key?" FAQ entry you must now create one through the App Center.

Why am I unable to see the Register an API link and how can I create an application key?

All registrations are now configured through the App Center. Previously you were able to generate an Application Key and/or create an OAuth registration within your Exact Online.

In Exact Online you can create an app registration for private use (customer account) or an app registration for commercial use (partner account). Go to Target groups and site maps for more information.

If the Register API Key link is not visible in the App Center menu you do not have the correct rights to view it. To make the link visible go to, Username > My Exact Online > Rights and select Manage subscription.

License

Exact Online REST API Library in Python is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 3 or any later version.

TODO

  • Right now, the section-links in the README.rst do not work in PyPI: the quick jump links fail to emerge.
  • Replace base_url with response_url?
  • Add travis build stuff.

Further reading

Exact Online's Projects

Exact Online doesnโ€™t have any public repositories yet.

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.