You've already forked akaunting-py
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f10b39a3ba | |||
| bbd2083109 | |||
| 2c6c1f6f34 | |||
| d06b7d0a76 | |||
| c92fb0d1ee | |||
| 96d4523ef4 | |||
| d57798a9c0 | |||
| 88f161082d | |||
| e0e803f1a7 | |||
| 6a03577605 | |||
| 7eaf7c99dd | |||
| 409ea90a4b | |||
| d7f4e25830 | |||
| db70f981ad | |||
| af0c6b504f |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -6,6 +6,14 @@ __pycache__
|
|||||||
.coverage
|
.coverage
|
||||||
coverage.xml
|
coverage.xml
|
||||||
|
|
||||||
|
# distribution files
|
||||||
|
dist/
|
||||||
|
*egg-info/
|
||||||
|
|
||||||
# Ignore Scratch files
|
# Ignore Scratch files
|
||||||
scratch/*
|
scratch/*
|
||||||
!/scratch/README.md
|
!/scratch/README.md
|
||||||
|
|
||||||
|
# Ignore virtualenv
|
||||||
|
.venv
|
||||||
|
venv
|
||||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
16
.idea/akaunting-py.iml
generated
Normal file
16
.idea/akaunting-py.iml
generated
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.pytest_cache" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="PyDocumentationSettings">
|
||||||
|
<option name="format" value="PLAIN" />
|
||||||
|
<option name="myDocStringFormat" value="Plain" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
4
.idea/misc.xml
generated
Normal file
4
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (akaunting-py)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/akaunting-py.iml" filepath="$PROJECT_DIR$/.idea/akaunting-py.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
from akauntingpy.api import Client
|
from akauntingpy.api import Client
|
||||||
from akauntingpy.exceptions import *
|
from akauntingpy.exceptions import *
|
||||||
|
|
||||||
|
__version__ = '1.0.0'
|
||||||
@@ -1,63 +1,79 @@
|
|||||||
import requests
|
import requests
|
||||||
import json
|
gimport logging
|
||||||
from requests.auth import HTTPBasicAuth
|
from requests.auth import HTTPBasicAuth
|
||||||
|
|
||||||
from akauntingpy import exceptions
|
from akauntingpy import exceptions
|
||||||
from akauntingpy.helpers import *
|
from akauntingpy.helpers import *
|
||||||
|
|
||||||
|
__version__ = "1.0.5"
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
class Client(object):
|
||||||
"""
|
"""
|
||||||
Akaunting interface.
|
Akaunting interface.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
url,
|
url,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
company_id):
|
company_id,
|
||||||
|
ssl_verify=True,
|
||||||
|
currency_code="NZD",
|
||||||
|
currency_rate="1.0"):
|
||||||
"""
|
"""
|
||||||
Create a new instance.
|
Create a new instance.
|
||||||
Args:
|
Args:
|
||||||
url (str): The URL to the Akaunting api.
|
url (str): The URL to the Akaunting api. ** required **
|
||||||
username (str): The username of the Akaunting credentials.
|
username (str): The username of the Akaunting credentials. ** required **
|
||||||
password (str): The password of the Akaunting credentials.
|
password (str): The password of the Akaunting credentials. ** required **
|
||||||
|
company_id (str): The company ID from Akaunting
|
||||||
|
currency_code (str): The currency code. default is NZD
|
||||||
|
currency_rate (str): The currency rate. default is "1.0"
|
||||||
"""
|
"""
|
||||||
self.url = url
|
self.url = url
|
||||||
|
self.ssl_verify = ssl_verify
|
||||||
self.authentication = HTTPBasicAuth(username, password)
|
self.authentication = HTTPBasicAuth(username, password)
|
||||||
self.headers = {
|
self.headers = {
|
||||||
'User-Agent': 'AkauntingPy 1.0',
|
'User-Agent': 'AkauntingPy v' + __version__,
|
||||||
}
|
}
|
||||||
self.company_id = company_id
|
self.company_id = company_id
|
||||||
self.default_params = {'company_id': company_id}
|
self.default_params = {'company_id': company_id}
|
||||||
|
self.currency_code = currency_code
|
||||||
|
self.currency_rate = currency_rate
|
||||||
|
|
||||||
def call(self, method="get", endpoint=None,
|
def call(self, method="get", endpoint=None,
|
||||||
**params) -> dict():
|
**params) -> dict:
|
||||||
response = None
|
|
||||||
|
|
||||||
response = requests.request(method=method,
|
response = requests.request(method=method,
|
||||||
url=self.url + "/" + endpoint,
|
url=self.url + "/" + endpoint,
|
||||||
headers=self.headers,
|
headers=self.headers,
|
||||||
auth=self.authentication,
|
auth=self.authentication,
|
||||||
params=MergeDict(self.default_params, params)
|
params=MergeDict(self.default_params, params),
|
||||||
|
verify=self.ssl_verify
|
||||||
)
|
)
|
||||||
|
|
||||||
print(response)
|
|
||||||
response_ = response.json()
|
response_ = response.json()
|
||||||
print(response_)
|
|
||||||
|
|
||||||
if response.status_code == 401:
|
if response.status_code == 401:
|
||||||
raise exceptions.MissingPermission(response_['message'])
|
raise exceptions.MissingPermission(response_['message'])
|
||||||
elif response.status_code == 422:
|
elif response.status_code == 422:
|
||||||
errors = []
|
errors = []
|
||||||
for key in response_['errors']:
|
for key in response_['errors']:
|
||||||
errors.append(RemoveFromString(["<strong>","</strong>"],
|
errors.append(RemoveFromString(["<strong>", "</strong>"],
|
||||||
response_['errors'][key][0])
|
response_['errors'][key][0])
|
||||||
+ " (" + key + ")")
|
+ " (" + key + ")")
|
||||||
raise exceptions.InvalidData(response_['message'] +
|
raise exceptions.InvalidData(response_['message'] +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\n".join(errors))
|
"\n".join(errors))
|
||||||
# elif response.status_code != 200 and response.status_code != 201:
|
# elif response.status_code != 200 and response.status_code != 201:
|
||||||
# raise exceptions.Error(response_['message'])
|
# raise exceptions.Error(response_['message'])
|
||||||
|
elif response.status_code == 429:
|
||||||
|
# We hit the maximum requests
|
||||||
|
raise exceptions.TooManyAttempts(response_['message'])
|
||||||
return response_
|
return response_
|
||||||
|
|
||||||
def ping(self):
|
def ping(self):
|
||||||
@@ -65,30 +81,73 @@ class Client(object):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def get_accounts(self, **params):
|
def get_accounts(self, **params):
|
||||||
data = self.call(endpoint="accounts")
|
data = self.call(endpoint="accounts", **params)
|
||||||
|
|
||||||
|
if params.get('search', False):
|
||||||
|
# Check if there is an account returned
|
||||||
|
try:
|
||||||
|
if data['meta']['pagination'].get('count') == 0:
|
||||||
|
# No account found
|
||||||
|
raise exceptions.AccountNotFound("Sorry, account not found matching search parameters: %s".format(
|
||||||
|
params.get('search')
|
||||||
|
))
|
||||||
|
except KeyError as e:
|
||||||
|
# New API 3.0
|
||||||
|
if data['meta']['total'] == 0:
|
||||||
|
raise exceptions.AccountNotFound("Sorry, account not found matching search parameters: %s".format(
|
||||||
|
params.get('search')
|
||||||
|
))
|
||||||
|
|
||||||
|
return data['data']
|
||||||
|
|
||||||
|
def get_contact(self, **params):
|
||||||
|
data = self.call(endpoint="contacts", **params)
|
||||||
|
print(data)
|
||||||
|
if params.get('search', False):
|
||||||
|
try:
|
||||||
|
# Check if there is an account returned
|
||||||
|
if data['meta']['pagination'].get('count') == 0:
|
||||||
|
# No account found
|
||||||
|
raise exceptions.AccountNotFound("Sorry, contact not found matching search parameters: %s".format(
|
||||||
|
params.get('search')
|
||||||
|
))
|
||||||
|
except KeyError as e:
|
||||||
|
# New API 3.0
|
||||||
|
if data['meta']['total'] == 0:
|
||||||
|
raise exceptions.AccountNotFound("Sorry, contact not found matching search parameters: %s".format(
|
||||||
|
params.get('search')
|
||||||
|
))
|
||||||
|
|
||||||
return data['data']
|
return data['data']
|
||||||
|
|
||||||
def create_transaction(self,
|
def create_transaction(self,
|
||||||
type='income', # Payment method type
|
transaction_type='income', # Payment method type
|
||||||
account_id=None, # Account ID to assign
|
account_id=None, # Account ID to assign
|
||||||
category_id=None, # Category ID to assign
|
number="NULL", # Transaction number
|
||||||
contact_id=None, # Contact ID/Client to assign
|
category_id=None, # Category ID to assign
|
||||||
description=None, # Description
|
contact_id=None, # Contact ID/Client to assign
|
||||||
paid_at=None, # Date of expense/transfer or income
|
description=None, # Description
|
||||||
reference=None, # Reference for the payment
|
paid_at=None, # Date of expense/transfer or income
|
||||||
payment_method=None, # Payment method
|
reference=None, # Reference for the payment
|
||||||
currency_code=None, # Currency code
|
payment_method=None, # Payment method
|
||||||
currency_rate=None, # Currency rate
|
currency_code=None, # Currency code
|
||||||
amount=None, # Amount received/paid
|
currency_rate=None, # Currency rate
|
||||||
**params # Any additional parameters
|
amount=None, # Amount received/paid
|
||||||
):
|
**params # Any additional parameters
|
||||||
|
):
|
||||||
|
|
||||||
|
if currency_code is None:
|
||||||
|
# Set default value from class
|
||||||
|
currency_code = self.currency_code
|
||||||
|
if currency_rate is None:
|
||||||
|
# Set default value from class
|
||||||
|
currency_rate = self.currency_rate
|
||||||
|
|
||||||
data = self.call(endpoint="transactions",
|
data = self.call(endpoint="transactions",
|
||||||
method="POST",
|
method="POST",
|
||||||
search="type:" + type,
|
search="type:" + transaction_type,
|
||||||
type=type,
|
number=number,
|
||||||
|
type=transaction_type,
|
||||||
account_id=account_id,
|
account_id=account_id,
|
||||||
category_id=category_id,
|
category_id=category_id,
|
||||||
paid_at=paid_at,
|
paid_at=paid_at,
|
||||||
@@ -97,8 +156,34 @@ class Client(object):
|
|||||||
reference=reference,
|
reference=reference,
|
||||||
currency_code=currency_code,
|
currency_code=currency_code,
|
||||||
currency_rate=currency_rate,
|
currency_rate=currency_rate,
|
||||||
amount=amount,
|
amount=EnsurePositiveInteger(amount),
|
||||||
description=description,
|
description=description,
|
||||||
**params
|
**params
|
||||||
)
|
)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def create_transfer(self,
|
||||||
|
from_account_id=None, # Account ID to create transfer from
|
||||||
|
to_account_id=None, # Account ID to create transfer to
|
||||||
|
transferred_at=None, # Date of expense/transfer or income
|
||||||
|
payment_method="Bank Transfer", # Payment method
|
||||||
|
amount=None, # Amount received/paid
|
||||||
|
**params # Any additional parameters
|
||||||
|
):
|
||||||
|
logger.info("Transfer called with parameters")
|
||||||
|
logger.info("from_account_id: %s", from_account_id)
|
||||||
|
logger.info("to_account_id: %s", to_account_id)
|
||||||
|
logger.info("transferred_at: %s", transferred_at)
|
||||||
|
logger.info("payment_method: %s", payment_method)
|
||||||
|
logger.info("amount: %s", amount)
|
||||||
|
|
||||||
|
data = self.call(endpoint="transfers",
|
||||||
|
method="POST",
|
||||||
|
from_account_id=from_account_id,
|
||||||
|
to_account_id=to_account_id,
|
||||||
|
transferred_at=transferred_at,
|
||||||
|
payment_method=payment_method,
|
||||||
|
amount=EnsurePositiveInteger(amount),
|
||||||
|
**params
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|||||||
@@ -18,3 +18,27 @@ class InvalidData(Error):
|
|||||||
Args:
|
Args:
|
||||||
Error (_type_): _description_
|
Error (_type_): _description_
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class AccountNotFound(Error):
|
||||||
|
"""
|
||||||
|
Account not found
|
||||||
|
|
||||||
|
Args:
|
||||||
|
Error (_type_): _description_
|
||||||
|
"""
|
||||||
|
|
||||||
|
class ContactNotFound(Error):
|
||||||
|
"""
|
||||||
|
Account not found
|
||||||
|
|
||||||
|
Args:
|
||||||
|
Error (_type_): _description_
|
||||||
|
"""
|
||||||
|
|
||||||
|
class TooManyAttempts(Error):
|
||||||
|
"""
|
||||||
|
Too Many attempts to API
|
||||||
|
|
||||||
|
Args:
|
||||||
|
Error (_type_): _description_
|
||||||
|
"""
|
||||||
@@ -7,3 +7,6 @@ def RemoveFromString(items, string):
|
|||||||
for item in items:
|
for item in items:
|
||||||
string = string.replace(item, '')
|
string = string.replace(item, '')
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
def EnsurePositiveInteger(number):
|
||||||
|
return float(number) if float(number) > 0 else (float(number) * -1)
|
||||||
17
data/CreateTransferSuccess.json
Normal file
17
data/CreateTransferSuccess.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"data":{
|
||||||
|
"id":2,
|
||||||
|
"company_id":1,
|
||||||
|
"from_account":"Some Account",
|
||||||
|
"from_account_id":1,
|
||||||
|
"to_account":"Some Account New",
|
||||||
|
"to_account_id":2,
|
||||||
|
"paid_at":"2022-05-16T11:57:51+12:00",
|
||||||
|
"amount":100,
|
||||||
|
"amount_formatted":"$100.00",
|
||||||
|
"currency_code":"NZD",
|
||||||
|
"created_by":1,
|
||||||
|
"created_at":"2022-05-16T11:57:51+12:00",
|
||||||
|
"updated_at":"2022-05-16T11:57:51+12:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
34
data/v2/GetAccountsSearch.json
Normal file
34
data/v2/GetAccountsSearch.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"data":[
|
||||||
|
{
|
||||||
|
"id":2,
|
||||||
|
"company_id":1,
|
||||||
|
"name":"Some Account",
|
||||||
|
"number":"00-0000-0000000-00",
|
||||||
|
"currency_code":"NZD",
|
||||||
|
"opening_balance":0,
|
||||||
|
"opening_balance_formatted":"$0.00",
|
||||||
|
"current_balance":100,
|
||||||
|
"current_balance_formatted":"$100.00",
|
||||||
|
"bank_name":"A Bank",
|
||||||
|
"bank_phone":"None",
|
||||||
|
"bank_address":"None",
|
||||||
|
"enabled":true,
|
||||||
|
"created_by":1,
|
||||||
|
"created_at":"2022-05-13T16:16:16+12:00",
|
||||||
|
"updated_at":"2022-05-13T16:16:16+12:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta":{
|
||||||
|
"pagination":{
|
||||||
|
"total":1,
|
||||||
|
"count":1,
|
||||||
|
"per_page":25,
|
||||||
|
"current_page":1,
|
||||||
|
"total_pages":1,
|
||||||
|
"links":{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
data/v2/GetAccountsSearchNotFound.json
Normal file
15
data/v2/GetAccountsSearchNotFound.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"data":[],
|
||||||
|
"meta":{
|
||||||
|
"pagination":{
|
||||||
|
"total":0,
|
||||||
|
"count":0,
|
||||||
|
"per_page":25,
|
||||||
|
"current_page":0,
|
||||||
|
"total_pages":0,
|
||||||
|
"links":{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
data/v3/GetAccountsSearch.json
Normal file
56
data/v3/GetAccountsSearch.json
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"data":[
|
||||||
|
{
|
||||||
|
"id":2,
|
||||||
|
"company_id":1,
|
||||||
|
"type":"bank",
|
||||||
|
"name":"Some Account",
|
||||||
|
"number":"00-0000-0000000-00",
|
||||||
|
"currency_code":"NZD",
|
||||||
|
"opening_balance":351.17,
|
||||||
|
"opening_balance_formatted":"$351.17",
|
||||||
|
"current_balance":306.3600000000006,
|
||||||
|
"current_balance_formatted":"$306.36",
|
||||||
|
"bank_name":"None",
|
||||||
|
"bank_phone":"None",
|
||||||
|
"bank_address":"None",
|
||||||
|
"enabled":true,
|
||||||
|
"created_from":"core::ui",
|
||||||
|
"created_by":1,
|
||||||
|
"created_at":"2022-05-30T11:06:21+12:00",
|
||||||
|
"updated_at":"2022-05-30T11:10:47+12:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links":{
|
||||||
|
"first":"https://someakaunting-url/api/accounts?page=1",
|
||||||
|
"last":"https://someakaunting-url/api/accounts?page=1",
|
||||||
|
"prev":"None",
|
||||||
|
"next":"None"
|
||||||
|
},
|
||||||
|
"meta":{
|
||||||
|
"current_page":1,
|
||||||
|
"from":1,
|
||||||
|
"last_page":1,
|
||||||
|
"links":[
|
||||||
|
{
|
||||||
|
"url":"None",
|
||||||
|
"label":"Previous",
|
||||||
|
"active":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url":"https://someakaunting-url/api/accounts?page=1",
|
||||||
|
"label":"1",
|
||||||
|
"active":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url":"None",
|
||||||
|
"label":"Next",
|
||||||
|
"active":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"path":"https://someakaunting-url/api/accounts",
|
||||||
|
"per_page":100,
|
||||||
|
"to":1,
|
||||||
|
"total":1
|
||||||
|
}
|
||||||
|
}
|
||||||
13
data/v3/GetAccountsSearchNotFound.json
Normal file
13
data/v3/GetAccountsSearchNotFound.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"data":[],
|
||||||
|
"meta":{
|
||||||
|
"total":0,
|
||||||
|
"count":0,
|
||||||
|
"per_page":100,
|
||||||
|
"current_page":0,
|
||||||
|
"total_pages":0,
|
||||||
|
"links":{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
requests >= 2.21.0
|
requests ~= 2.27.0
|
||||||
pytest
|
requests-mock ~= 1.9.3
|
||||||
|
pytest ~= 7.1.2
|
||||||
|
pytest-cov ~= 3.0
|
||||||
|
setuptools-scm ~= 6.4.2
|
||||||
|
|||||||
14
setup.py
14
setup.py
@@ -6,22 +6,22 @@ with open('README.md', 'r') as readme:
|
|||||||
long_description = readme.read()
|
long_description = readme.read()
|
||||||
|
|
||||||
|
|
||||||
version = '0.0.1'
|
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='akauntingpy',
|
name='akaunting-py',
|
||||||
version=version,
|
use_scm_version=True,
|
||||||
author='CyberCinch',
|
author='CyberCinch',
|
||||||
description='Python interface to the Akaunting API.',
|
description='Python interface to the Akaunting API.',
|
||||||
url='https://github.com/cybercinch/akaunting-py',
|
url='https://github.com/cybercinch/akaunting-py',
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type='text/markdown',
|
long_description_content_type='text/markdown',
|
||||||
license='MIT',
|
license='MIT',
|
||||||
|
version='1.0.5',
|
||||||
keywords='akaunting api library',
|
keywords='akaunting api library',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'requests >= 2.21.0',
|
'requests >= 2.21.0',
|
||||||
|
'setuptools-scm >= 6.4.2'
|
||||||
],
|
],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
@@ -29,8 +29,8 @@ setup(
|
|||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
],
|
],
|
||||||
command_options={
|
command_options={
|
||||||
'build_sphinx': {
|
# 'build_sphinx': {
|
||||||
'version': ('setup.py', version),
|
# 'version': ('setup.py', version),
|
||||||
},
|
# },
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,51 +1,51 @@
|
|||||||
import sys
|
import sys
|
||||||
from this import d
|
|
||||||
sys.path.insert(1, '.')
|
sys.path.insert(1, '.')
|
||||||
import pytest
|
from helpers import RetrieveJSONFromFile
|
||||||
import akauntingpy
|
import akauntingpy
|
||||||
from akauntingpy.exceptions import *
|
from akauntingpy.exceptions import *
|
||||||
from helpers import RetrieveJSONFromFile
|
import pytest
|
||||||
from requests.auth import HTTPBasicAuth
|
from requests.auth import HTTPBasicAuth
|
||||||
|
|
||||||
|
|
||||||
class TestAPI:
|
class TestAPI:
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
c = akauntingpy.Client("https://akaunting.guise.net.nz/api",
|
c = akauntingpy.Client("https://someakaunting-url/api",
|
||||||
"aaron@guise.net.nz",
|
"some-emailaddress@somewhere.com",
|
||||||
"L3Tm31N0w",
|
"aPassWord",
|
||||||
1)
|
1)
|
||||||
yield c
|
yield c
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def setUpFailed(self):
|
def setUpFailed(self):
|
||||||
c = akauntingpy.Client("https://akaunting.guise.net.nz/api",
|
c = akauntingpy.Client("https://someakaunting-url/api",
|
||||||
"aaron@guise.net.nz",
|
"some-emailaddress@somewhere.com",
|
||||||
"L3Tm31N0w1",
|
"aWrongPassWord",
|
||||||
1)
|
1)
|
||||||
yield c
|
yield c
|
||||||
|
|
||||||
def test_init(self, setUp):
|
def test_init(self, setUp):
|
||||||
c = setUp
|
c = setUp
|
||||||
assert isinstance(c, akauntingpy.Client)
|
assert isinstance(c, akauntingpy.Client)
|
||||||
assert c.url == "https://akaunting.guise.net.nz/api"
|
assert c.url == "https://someakaunting-url/api"
|
||||||
assert isinstance(c.authentication, HTTPBasicAuth)
|
assert isinstance(c.authentication, HTTPBasicAuth)
|
||||||
|
|
||||||
def test_ping_success(self, setUp, requests_mock):
|
def test_ping_success(self, setUp, requests_mock):
|
||||||
c = setUp
|
c = setUp
|
||||||
requests_mock.get(c.url + '/ping',
|
requests_mock.get(c.url + '/ping',
|
||||||
json=RetrieveJSONFromFile("data/pingSuccess.json"),
|
json=RetrieveJSONFromFile("data/pingSuccess.json"),
|
||||||
status_code=200)
|
status_code=200)
|
||||||
data = c.ping()
|
data = c.ping()
|
||||||
# The returned data should have status = ok
|
# The returned data should have status = ok
|
||||||
assert data['status'] == 'ok'
|
assert data['status'] == 'ok'
|
||||||
|
|
||||||
def test_ping_failed(self, setUpFailed, requests_mock):
|
def test_ping_failed(self, setUpFailed, requests_mock):
|
||||||
c = setUpFailed
|
c = setUpFailed
|
||||||
requests_mock.get(c.url + '/ping',
|
requests_mock.get(c.url + '/ping',
|
||||||
json=RetrieveJSONFromFile("data/pingFailure.json"),
|
json=RetrieveJSONFromFile("data/pingFailure.json"),
|
||||||
status_code=401)
|
status_code=401)
|
||||||
with pytest.raises(MissingPermission, match="Invalid credentials."):
|
with pytest.raises(MissingPermission, match="Invalid credentials."):
|
||||||
|
|
||||||
data = c.ping()
|
data = c.ping()
|
||||||
assert data['status_code'] == 401
|
assert data['status_code'] == 401
|
||||||
|
|
||||||
@@ -55,23 +55,49 @@ class TestAPI:
|
|||||||
json=RetrieveJSONFromFile("data/GetAccountsList.json"))
|
json=RetrieveJSONFromFile("data/GetAccountsList.json"))
|
||||||
data = c.get_accounts(params={'page': 1, 'limit': 200})
|
data = c.get_accounts(params={'page': 1, 'limit': 200})
|
||||||
|
|
||||||
|
def test_get_account_search_v2(self, setUp, requests_mock):
|
||||||
|
c = setUp
|
||||||
|
requests_mock.get(c.url + "/accounts?search=number%3A00-0000-0000000-00¶ms=page¶ms=limit&company_id=1",
|
||||||
|
json=RetrieveJSONFromFile("data/v2/GetAccountsSearch.json"))
|
||||||
|
data = c.get_accounts(search="number:00-0000-0000000-00", params={'page': 1, 'limit': 200})
|
||||||
|
|
||||||
|
def test_get_account_search_v3(self, setUp, requests_mock):
|
||||||
|
c = setUp
|
||||||
|
requests_mock.get(c.url + "/accounts?search=number%3A00-0000-0000000-00¶ms=page¶ms=limit&company_id=1",
|
||||||
|
json=RetrieveJSONFromFile("data/v3/GetAccountsSearch.json"))
|
||||||
|
data = c.get_accounts(search="number:00-0000-0000000-00", params={'page': 1, 'limit': 200})
|
||||||
|
|
||||||
|
def test_get_account_search_not_found_v2(self, setUp, requests_mock):
|
||||||
|
c = setUp
|
||||||
|
requests_mock.get(c.url + "/accounts?search=number%3Aarandomvalue&company_id=1",
|
||||||
|
json=RetrieveJSONFromFile("data/v2/GetAccountsSearchNotFound.json"))
|
||||||
|
with pytest.raises(AccountNotFound):
|
||||||
|
data = c.get_accounts(search="number:arandomvalue")
|
||||||
|
|
||||||
|
def test_get_account_search_not_found_v3(self, setUp, requests_mock):
|
||||||
|
c = setUp
|
||||||
|
requests_mock.get(c.url + "/accounts?search=number%3Aarandomvalue&company_id=1",
|
||||||
|
json=RetrieveJSONFromFile("data/v3/GetAccountsSearchNotFound.json"))
|
||||||
|
with pytest.raises(AccountNotFound):
|
||||||
|
data = c.get_accounts(search="number:arandomvalue")
|
||||||
|
|
||||||
def test_create_transaction_income_success(self, setUp, requests_mock,
|
def test_create_transaction_income_success(self, setUp, requests_mock,
|
||||||
type='income', # Payment method type
|
transaction_type='income', # Payment method type
|
||||||
account_id=2, # Account ID to assign
|
account_id=2, # Account ID to assign
|
||||||
category_id=3, # Category ID to assign
|
category_id=3, # Category ID to assign
|
||||||
contact_id=None, # Contact ID/Client to assign
|
contact_id=None, # Contact ID/Client to assign
|
||||||
paid_at="2022-05-15", # Date of expense/transfer or income
|
paid_at="2022-05-15", # Date of expense/transfer or income
|
||||||
reference=None, # Reference for the payment
|
reference=None, # Reference for the payment
|
||||||
payment_method="bank_transfer", # Payment method
|
payment_method="bank_transfer", # Payment method
|
||||||
currency_code="NZD", # Currency code
|
currency_code="NZD", # Currency code
|
||||||
currency_rate=1, # Currency rate
|
currency_rate=1, # Currency rate
|
||||||
amount="25.00", # Amount received/paid
|
amount="25.00", # Amount received/paid
|
||||||
):
|
):
|
||||||
c = setUp
|
c = setUp
|
||||||
requests_mock.post(c.url + "/transactions",
|
requests_mock.post(c.url + "/transactions",
|
||||||
json=RetrieveJSONFromFile("data/CreateTransactionIncomeSuccess.json"),
|
json=RetrieveJSONFromFile("data/CreateTransactionIncomeSuccess.json"),
|
||||||
status_code=201)
|
status_code=201)
|
||||||
data = c.create_transaction(type=type,
|
data = c.create_transaction(transaction_type=transaction_type,
|
||||||
account_id=account_id,
|
account_id=account_id,
|
||||||
category_id=category_id,
|
category_id=category_id,
|
||||||
paid_at=paid_at,
|
paid_at=paid_at,
|
||||||
@@ -84,17 +110,43 @@ class TestAPI:
|
|||||||
description="Some description of the transaction"
|
description="Some description of the transaction"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_transaction_income_failed(self, setUp, requests_mock,
|
def test_create_transaction_income_default_currency_success(self, setUp, requests_mock,
|
||||||
contact_id=None, # Contact ID/Client to assign
|
transaction_type='income', # Payment method type
|
||||||
reference=None, # Reference for the payment
|
account_id=2, # Account ID to assign
|
||||||
currency_code="NZD", # Currency code
|
category_id=3, # Category ID to assign
|
||||||
currency_rate=1, # Currency rate
|
contact_id=None, # Contact ID/Client to assign
|
||||||
amount="25.00", # Amount received/paid
|
paid_at="2022-05-15",
|
||||||
):
|
# Date of expense/transfer or income
|
||||||
|
reference=None, # Reference for the payment
|
||||||
|
payment_method="bank_transfer", # Payment method
|
||||||
|
amount="25.00", # Amount received/paid
|
||||||
|
):
|
||||||
c = setUp
|
c = setUp
|
||||||
requests_mock.post(c.url + "/transactions",
|
requests_mock.post(c.url + "/transactions",
|
||||||
json=RetrieveJSONFromFile("data/CreateTransactionIncomeFailed.json"),
|
json=RetrieveJSONFromFile("data/CreateTransactionIncomeSuccess.json"),
|
||||||
status_code=422)
|
status_code=201)
|
||||||
|
data = c.create_transaction(transaction_type=transaction_type,
|
||||||
|
account_id=account_id,
|
||||||
|
category_id=category_id,
|
||||||
|
paid_at=paid_at,
|
||||||
|
contact_id=contact_id,
|
||||||
|
payment_method=payment_method,
|
||||||
|
reference=reference,
|
||||||
|
amount=amount,
|
||||||
|
description="Some description of the transaction"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_create_transaction_income_failed(self, setUp, requests_mock,
|
||||||
|
contact_id=None, # Contact ID/Client to assign
|
||||||
|
reference=None, # Reference for the payment
|
||||||
|
currency_code="NZD", # Currency code
|
||||||
|
currency_rate=1, # Currency rate
|
||||||
|
amount="25.00", # Amount received/paid
|
||||||
|
):
|
||||||
|
c = setUp
|
||||||
|
requests_mock.post(c.url + "/transactions",
|
||||||
|
json=RetrieveJSONFromFile("data/CreateTransactionIncomeFailed.json"),
|
||||||
|
status_code=422)
|
||||||
with pytest.raises(InvalidData, match="The given data was invalid.*"):
|
with pytest.raises(InvalidData, match="The given data was invalid.*"):
|
||||||
data = c.create_transaction(contact_id=contact_id,
|
data = c.create_transaction(contact_id=contact_id,
|
||||||
reference=reference,
|
reference=reference,
|
||||||
@@ -106,10 +158,10 @@ class TestAPI:
|
|||||||
|
|
||||||
def test_create_transaction_expense_success(self, setUp, requests_mock):
|
def test_create_transaction_expense_success(self, setUp, requests_mock):
|
||||||
c = setUp
|
c = setUp
|
||||||
requests_mock.post(c.url + "/transactions",
|
requests_mock.post(c.url + "/transactions?search=type%3Aexpense&type=expense&account_id=3&category_id=4&paid_at=2022-05-16&payment_method=Bank+Transfer¤cy_code=NZD¤cy_rate=1&amount=100.0&description=Some+expenditures&company_id=1",
|
||||||
json=RetrieveJSONFromFile("data/CreateTransactionExpenseSuccess.json"),
|
json=RetrieveJSONFromFile("data/CreateTransactionExpenseSuccess.json"),
|
||||||
status_code=201)
|
status_code=201)
|
||||||
data = c.create_transaction(type="expense",
|
data = c.create_transaction(transaction_type="expense",
|
||||||
amount=100.00,
|
amount=100.00,
|
||||||
account_id=3,
|
account_id=3,
|
||||||
paid_at="2022-05-16",
|
paid_at="2022-05-16",
|
||||||
@@ -120,3 +172,18 @@ class TestAPI:
|
|||||||
description="Some expenditures"
|
description="Some expenditures"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_transfer_success(self, setUp, requests_mock):
|
||||||
|
c = setUp
|
||||||
|
requests_mock.post(c.url + "/transfers",
|
||||||
|
json=RetrieveJSONFromFile("data/CreateTransferSuccess.json"),
|
||||||
|
status_code=201)
|
||||||
|
data = c.create_transfer( amount=100.00,
|
||||||
|
account_id=3,
|
||||||
|
paid_at="2022-05-16",
|
||||||
|
currency_rate=1,
|
||||||
|
currency_code="NZD",
|
||||||
|
payment_method="Bank Transfer",
|
||||||
|
category_id="4",
|
||||||
|
description="Some expenditures"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user