Initial commit
This commit is contained in:
commit
c56243589b
0
CHANGELOG.md
Normal file
0
CHANGELOG.md
Normal file
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Python Invoice Ninja SDK
|
||||||
|
|
||||||
|
Inspired by the [official PHP SDK](https://github.com/invoiceninja/sdk-php), a
|
||||||
|
Python wrapper for Invoice Ninja's REST API.
|
||||||
|
|
||||||
74
invoice_ninja/__init__.py
Normal file
74
invoice_ninja/__init__.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
class InvoiceNinja(object):
|
||||||
|
API_V1 = 'api/v1'
|
||||||
|
ENDPOINT_URLS = {
|
||||||
|
'clients': 'clients',
|
||||||
|
'products': 'products',
|
||||||
|
'invoices': 'invoices',
|
||||||
|
'recurring invoices': 'recurring_invoices',
|
||||||
|
'payments': 'payments',
|
||||||
|
'quotes': 'quotes',
|
||||||
|
'credits': 'credits',
|
||||||
|
'reports': 'reports',
|
||||||
|
'activities': 'activities',
|
||||||
|
'charts': 'charts',
|
||||||
|
'companies': 'companies',
|
||||||
|
'documents': 'documents',
|
||||||
|
'emails': 'emails',
|
||||||
|
'expense': 'expenses',
|
||||||
|
'export': 'export',
|
||||||
|
'import': 'import_json',
|
||||||
|
'ping': 'ping',
|
||||||
|
'health check': 'health_check',
|
||||||
|
'users': 'users'
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
endpoint_url: str = 'https://invoicing.co',
|
||||||
|
api_token: str = str()):
|
||||||
|
self.endpoint_url = '{}/{}'.format(endpoint_url, self.API_V1)
|
||||||
|
self.api_token = api_token
|
||||||
|
self.headers = dict()
|
||||||
|
|
||||||
|
def _get_url_for(self, endpoint: str = 'ping'):
|
||||||
|
'''
|
||||||
|
Get complete URL for an endpoint.
|
||||||
|
|
||||||
|
Endpoint URLs are appended to the Invoice Ninja base URL
|
||||||
|
and returned.
|
||||||
|
'''
|
||||||
|
if endpoint in self.ENDPOINT_URLS:
|
||||||
|
return '{}/{}'.format(self.endpoint_url,
|
||||||
|
self.ENDPOINT_URLS[endpoint])
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise KeyError('Endpoint URL not found')
|
||||||
|
|
||||||
|
#def _get_headers(self, headers: dict = dict()):
|
||||||
|
def build_headers(self):
|
||||||
|
'''
|
||||||
|
Build Invoice Ninja API headers for request.
|
||||||
|
|
||||||
|
A header dictionary with the API token is returned by default.
|
||||||
|
'''
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'X-API-TOKEN': self.api_token,
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'}
|
||||||
|
|
||||||
|
return self.headers.update(headers)
|
||||||
|
|
||||||
|
def ping(self):
|
||||||
|
'''
|
||||||
|
Ping Invoice Ninja instance.
|
||||||
|
'''
|
||||||
|
server_response = requests.get(url=self._get_url_for(),
|
||||||
|
headers=self.build_headers())
|
||||||
|
|
||||||
|
if server_response.ok:
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
0
invoice_ninja/endpoints/__init__.py
Normal file
0
invoice_ninja/endpoints/__init__.py
Normal file
30
invoice_ninja/endpoints/base_endpoint.py
Normal file
30
invoice_ninja/endpoints/base_endpoint.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
from invoice_ninja import InvoiceNinja
|
||||||
|
|
||||||
|
class BaseEndpoint(InvoiceNinja):
|
||||||
|
def bulk(self, action: str):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def archive(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def restore(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def download(self):
|
||||||
|
pass
|
||||||
|
|
||||||
92
invoice_ninja/endpoints/clients.py
Normal file
92
invoice_ninja/endpoints/clients.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
from invoice_ninja.endpoints.base_endpoint import BaseEndpoint
|
||||||
|
from invoice_ninja.types.client import Client
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
class Clients(BaseEndpoint):
|
||||||
|
uri = '/api/v1/clients'
|
||||||
|
|
||||||
|
def __init__(self, base_url: str = str(), api_token: str = str()):
|
||||||
|
super().__init__(base_url, api_token)
|
||||||
|
self.url = super()._get_url_for('clients')
|
||||||
|
|
||||||
|
def __build_sort_params(self, sort: dict):
|
||||||
|
sort_params = {'sort': str()}
|
||||||
|
is_first_entry = True
|
||||||
|
for option in sort.keys():
|
||||||
|
if is_first_entry:
|
||||||
|
sort_params['sort'] += '{}|{}'.format(option, sort[option])
|
||||||
|
is_first_entry = False
|
||||||
|
|
||||||
|
else:
|
||||||
|
sort_params['sort'] += ' {}|{}'.format(option, sort[option])
|
||||||
|
|
||||||
|
return sort_params
|
||||||
|
|
||||||
|
def __client_from_dict(self, client: dict):
|
||||||
|
return Client(client_id=client['id'],
|
||||||
|
name=client['name'],
|
||||||
|
address=client['address1'],
|
||||||
|
city=client['city'],
|
||||||
|
state=client['state'],
|
||||||
|
postal_code=client['postal_code'],
|
||||||
|
phone=client['phone'],
|
||||||
|
email=client['contacts'][0]['email'],
|
||||||
|
pets=client['custom_value1'])
|
||||||
|
|
||||||
|
def __clients_from_response(self, response: requests.Response):
|
||||||
|
clients = list()
|
||||||
|
for client_dict in response.json()['data']:
|
||||||
|
clients.append(self.__client_from_dict(client_dict))
|
||||||
|
|
||||||
|
return clients
|
||||||
|
|
||||||
|
def show_client(self, client_id: str = None):
|
||||||
|
"""
|
||||||
|
Get client based on client id.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if client_id:
|
||||||
|
response = requests.get(url=self.url,
|
||||||
|
headers=super()._get_headers())
|
||||||
|
|
||||||
|
if response.ok:
|
||||||
|
return self.__client_from_dict(response.json()['data'])
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def list_clients(self, include: str = 'activities',
|
||||||
|
sort: dict = dict(), status: str = 'active',
|
||||||
|
name: str = None):
|
||||||
|
"""
|
||||||
|
Get list of clients.
|
||||||
|
"""
|
||||||
|
|
||||||
|
request_params = dict()
|
||||||
|
|
||||||
|
# Add sort parameters to request
|
||||||
|
if len(sort) > 0:
|
||||||
|
request_params.update(self.__build_sort_params(sort))
|
||||||
|
|
||||||
|
# Add include parameters to request
|
||||||
|
request_params.update({'include': include})
|
||||||
|
|
||||||
|
# Add status parameters to request
|
||||||
|
request_params.update({'status': status})
|
||||||
|
|
||||||
|
# Add name parameters to request
|
||||||
|
if name:
|
||||||
|
request_params.update({'name': name})
|
||||||
|
|
||||||
|
# Check is request should be sent with parameters
|
||||||
|
if len(request_params) > 0:
|
||||||
|
response = requests.get(url=self.url,
|
||||||
|
params=request_params,
|
||||||
|
headers=super()._get_headers())
|
||||||
|
else:
|
||||||
|
response = requests.get(url=self.url,
|
||||||
|
headers=super()._get_headers())
|
||||||
|
|
||||||
|
if response.ok:
|
||||||
|
return self.__clients_from_response(response)
|
||||||
|
|
||||||
0
invoice_ninja/models/README.md
Normal file
0
invoice_ninja/models/README.md
Normal file
0
invoice_ninja/models/__init__.py
Normal file
0
invoice_ninja/models/__init__.py
Normal file
27
invoice_ninja/models/client.py
Normal file
27
invoice_ninja/models/client.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
class Client(object):
|
||||||
|
def __init__(self, client_id: int = None, name: str = None,
|
||||||
|
address: str = None, city: str = None, state: str = None,
|
||||||
|
postal_code: str = None, phone: str = None,
|
||||||
|
email: str = None, pets: str = None):
|
||||||
|
self.id = client_id
|
||||||
|
self.name = name
|
||||||
|
self.address = address
|
||||||
|
self.city = city
|
||||||
|
self.state = state
|
||||||
|
self.postal_code = postal_code
|
||||||
|
self.phone = phone
|
||||||
|
self.email = email
|
||||||
|
self.pets = pets
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'Client({}, {}, {}, {}, {}, {}, {}, {}, {})'.format(
|
||||||
|
self.id,
|
||||||
|
self.name,
|
||||||
|
self.address,
|
||||||
|
self.city,
|
||||||
|
self.state,
|
||||||
|
self.postal_code,
|
||||||
|
self.phone,
|
||||||
|
self.email,
|
||||||
|
self.pets)
|
||||||
|
|
||||||
36
invoice_ninja/models/clientContact.py
Normal file
36
invoice_ninja/models/clientContact.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
class ClientContact(object):
|
||||||
|
def __init__(self, first_name: str = '',
|
||||||
|
last_name: str = '',
|
||||||
|
email: str = '',
|
||||||
|
phone: str = '',
|
||||||
|
send_email: bool = True,
|
||||||
|
custom_value1: str = '',
|
||||||
|
custom_value2: str = '',
|
||||||
|
custom_value3: str = '',
|
||||||
|
custom_value4: str = ''):
|
||||||
|
self.first_name = first_name
|
||||||
|
self.last_name = last_name
|
||||||
|
self.email = email
|
||||||
|
self.phone = phone
|
||||||
|
|
||||||
|
# Flag for whether the contact will receive emails.
|
||||||
|
self.send_email = send_email
|
||||||
|
|
||||||
|
# Custom values
|
||||||
|
self.custom_value1 = custom_value1
|
||||||
|
self.custom_value2 = custom_value2
|
||||||
|
self.custom_value3 = custom_value3
|
||||||
|
self.custom_value4 = custom_value4
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'ClientContact({}, {}, {}, {}, {}, {}, {}, {}, {})'.format(
|
||||||
|
self.first_name,
|
||||||
|
self.last_name,
|
||||||
|
self.email,
|
||||||
|
self.phone,
|
||||||
|
self.send_email,
|
||||||
|
self.custom_value1,
|
||||||
|
self.custom_value2,
|
||||||
|
self.custom_value3,
|
||||||
|
self.custom_value4)
|
||||||
|
|
||||||
38
invoice_ninja/models/clientSettings.py
Normal file
38
invoice_ninja/models/clientSettings.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
class ClientSettings(object):
|
||||||
|
def __init__(self, language_id: str = None,
|
||||||
|
currency_id: str = None,
|
||||||
|
payment_terms: str = None,
|
||||||
|
valid_until: str = None,
|
||||||
|
default_task_rate: float = 0,
|
||||||
|
send_reminders: bool = None):
|
||||||
|
# The language ID for the client, for the full list of languages,
|
||||||
|
# please see this resource - optional
|
||||||
|
#
|
||||||
|
# https://invoiceninja.github.io/docs/statics/#languages
|
||||||
|
self.language_id = language_id
|
||||||
|
|
||||||
|
# The currency ID - optional
|
||||||
|
# See this resource for full list:
|
||||||
|
#
|
||||||
|
# https://invoiceninja.github.io/docs/statics/#currencies
|
||||||
|
self.currency_id = currency_id
|
||||||
|
|
||||||
|
# The payment terms - in days - optional
|
||||||
|
self.payment_terms = payment_terms
|
||||||
|
|
||||||
|
# The quote terms - optional
|
||||||
|
#
|
||||||
|
# How many days the quote will be valid for.
|
||||||
|
self.valid_until = valid_until
|
||||||
|
|
||||||
|
# The task rate for this client - optional
|
||||||
|
#
|
||||||
|
# A value of 0 equates to disabled.
|
||||||
|
self.default_task_rate = default_task_rate
|
||||||
|
|
||||||
|
# Whether the client will receive reminders - optional
|
||||||
|
#
|
||||||
|
# When left unset, this setting will rely on the company
|
||||||
|
# settings as the override/default
|
||||||
|
self.send_reminders = send_reminders
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user