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