pwitab / visma Goto Github PK
View Code? Open in Web Editor NEWA Python Client library for integration to Visma eAccounting, Visma eEkonomi
Home Page: http://visma.readthedocs.io
License: BSD 3-Clause "New" or "Revised" License
A Python Client library for integration to Visma eAccounting, Visma eEkonomi
Home Page: http://visma.readthedocs.io
License: BSD 3-Clause "New" or "Revised" License
Some models have different schemas for posting and getting data from the API. This needs to be handled in a way that gives a good python api
When trying to create projects I get a Conflict error from visma API.
data = '{"CustomerId": null, "Status": 1, "Name": "TestProjec", "Number": "3", "EndDate": null, "StartDate": "2018-02-03", "Notes": null}'
Opened a support ticket with Visma.
On /object for example GET /customers you get the data in an enveloped reponse with metadata for handling pagination and the data.
We should have a way of defining the envelope format and return the data + the meta data in a proper way.
If we take the Account model for example, as of now we assume it to be /accounts for all accounts and /account/number to get a specific account.
But the model in visma needs a fiscal year as of /accounts/fiscal_year/number.
There are a number of other special cases like this in the api specification so it would be beneficial to have a generic way of handling these cases.
First we need to add constraints on the endpoints that needs extra information.
So we will get an error if doing Account.objects.get(number)
Example case would be to be create the manager to need the fiscal year on get, ex: Account.objects.get(pk=number, fiscal_year=fiscal_year)
But this might get tricky since Accounts also supports the accounts/standardaccounts and accounts/fiscal_year
So maybe better to add it in the query. Like Account.objects.all().with('standradaccounts') and Account.objects.all().with(fiscal_year=fiscal_year_object)
It would then also be able to add more paths with new with statements. But order would be held intact. Also it could support taking non primary key values from objects to use in the path.
Like: Account.objects.all().with('fiscal_year__name'=fiscal_year)
As of now when querying and the enpoint has many result which leads to pagination the pagination handler will go thorugh all available pages and get the results.
To handle this there should be a method .limit_result(int: xx) that will stop the pagination after the result has been collected.
It might also be a good idea to provide control over the pagination with a .paginate({paginate_settings}) that will just return the first page but if you call the .next() it will return the next page and so on.
Some endpoint only supports some functions. For example TermsOfPayment only supports GET_ALL (GET: /termsofpayment ) or GET_BY_ID ( GET /termsofpayment/{id}
The integration test of updating a CustomerInvoiceDraft is no longer passing.
Seems like it does not return the the updated object on PUT and GET on the object after update is also not reflecting the update.
I have so far only tested the field invoice_customer_name since the test is not for all fields but just for the updating flow. The PUT gets 200 OK.
Ett av mina integrationstester för Customer invoice Drafts går inte längre igenom.
GET to receive the CustomerInvoiceDraft return:
{"Id":"bc441d97-2f43-4d99-bb25-cf9dd0fb76e3","CustomerId":"a4247bd2-45d7-4a98-a69f-af40830deb67","CreatedUtc":"2018-07-27T13:08:08.1534715Z","IsCreditInvoice":false,"RotReducedInvoicingType":0,"RotReducedInvoicingPropertyName":null,"RotReducedInvoicingOrgNumber":null,"RotReducedInvoicingAmount":0.0,"RotReducedInvoicingAutomaticDistribution":false,"RotPropertyType":null,"HouseWorkOtherCosts":null,"Rows":[],"Persons":[],"YourReference":null,"OurReference":null,"InvoiceCustomerName":"TestCustomer AB","InvoiceAddress1":null,"InvoiceAddress2":null,"InvoicePostalCode":"25234","InvoiceCity":"Helsingborg","InvoiceCountryCode":"SE","InvoiceCurrencyCode":"SEK","DeliveryCustomerName":null,"DeliveryAddress1":null,"DeliveryAddress2":null,"DeliveryPostalCode":null,"DeliveryCity":null,"DeliveryCountryCode":null,"DeliveryMethodName":null,"DeliveryTermName":null,"DeliveryMethodCode":null,"DeliveryTermCode":null,"EuThirdParty":false,"CustomerIsPrivatePerson":false,"ReverseChargeOnConstructionServices":false,"SalesDocumentAttachments":[],"InvoiceDate":"2018-07-27T13:08:08.1534715Z","DeliveryDate":null,"TotalAmount":0.0,"TotalVatAmount":0.0,"TotalRoundings":0.0,"TotalAmountBaseCurrency":0.0,"TotalVatAmountBaseCurrency":0.0,"CustomerNumber":"107","IncludesVat":false}
"InvoiceCustomerName":"TestCustomer AB"
PUT to updated object sent:
{"DeliveryPostalCode": null, "InvoiceDate": "2018-07-27T13:08:08+00:00", "DeliveryAddress1": null, "RotReducedInvoicingOrgNumber": null, "DeliveryAddress2": null, "InvoiceCity": "Helsingborg", "DeliveryTermName": null, "IsCreditInvoice": false, "Rows": [], "InvoiceAddress2": null, "CustomerId": "a4247bd2-45d7-4a98-a69f-af40830deb67", "EuThirdParty": false, "Persons": [], "RotReducedInvoicingType": 0, "CustomerIsPrivatePerson": false, "DeliveryMethodName": null, "DeliveryCity": null, "RotReducedInvoicingAutomaticDistribution": false, "InvoiceAddress1": null, "YourReference": null, "RotReducedInvoicingAmount": 0.0, "InvoiceCustomerName": "Updated Name", "RotReducedInvoicingPropertyName": null, "OurReference": null, "DeliveryDate": null, "RotPropertyType": null, "DeliveryCountryCode": null, "DeliveryCustomerName": null, "DeliveryMethodCode": null, "InvoiceCountryCode": "SE", "HouseWorkOtherCosts": null, "DeliveryTermCode": null, "InvoicePostalCode": "25234"}
"InvoiceCustomerName": "Updated Name"
PUT response:
{"Id":"bc441d97-2f43-4d99-bb25-cf9dd0fb76e3","CustomerId":"a4247bd2-45d7-4a98-a69f-af40830deb67","CreatedUtc":"2018-07-27T13:08:08.1534715Z","IsCreditInvoice":false,"RotReducedInvoicingType":0,"RotReducedInvoicingPropertyName":null,"RotReducedInvoicingOrgNumber":null,"RotReducedInvoicingAmount":0.0,"RotReducedInvoicingAutomaticDistribution":false,"RotPropertyType":null,"HouseWorkOtherCosts":null,"Rows":[],"Persons":[],"YourReference":null,"OurReference":null,"InvoiceCustomerName":"TestCustomer AB","InvoiceAddress1":null,"InvoiceAddress2":null,"InvoicePostalCode":"25234","InvoiceCity":"Helsingborg","InvoiceCountryCode":"SE","InvoiceCurrencyCode":"SEK","DeliveryCustomerName":null,"DeliveryAddress1":null,"DeliveryAddress2":null,"DeliveryPostalCode":null,"DeliveryCity":null,"DeliveryCountryCode":null,"DeliveryMethodName":null,"DeliveryTermName":null,"DeliveryMethodCode":null,"DeliveryTermCode":null,"EuThirdParty":false,"CustomerIsPrivatePerson":false,"ReverseChargeOnConstructionServices":false,"SalesDocumentAttachments":[],"InvoiceDate":"2018-07-27T13:11:00.9984772Z","DeliveryDate":null,"TotalAmount":0.0,"TotalVatAmount":0.0,"TotalRoundings":0.0,"TotalAmountBaseCurrency":0.0,"TotalVatAmountBaseCurrency":0.0,"CustomerNumber":"107","IncludesVat":false}
"InvoiceCustomerName":"TestCustomer AB"
New get on object to verify update:
{"Id":"bc441d97-2f43-4d99-bb25-cf9dd0fb76e3","CustomerId":"a4247bd2-45d7-4a98-a69f-af40830deb67","CreatedUtc":"2018-07-27T13:08:08.1534715Z","IsCreditInvoice":false,"RotReducedInvoicingType":0,"RotReducedInvoicingPropertyName":null,"RotReducedInvoicingOrgNumber":null,"RotReducedInvoicingAmount":0.0,"RotReducedInvoicingAutomaticDistribution":false,"RotPropertyType":null,"HouseWorkOtherCosts":null,"Rows":[],"Persons":[],"YourReference":null,"OurReference":null,"InvoiceCustomerName":"TestCustomer AB","InvoiceAddress1":null,"InvoiceAddress2":null,"InvoicePostalCode":"25234","InvoiceCity":"Helsingborg","InvoiceCountryCode":"SE","InvoiceCurrencyCode":"SEK","DeliveryCustomerName":null,"DeliveryAddress1":null,"DeliveryAddress2":null,"DeliveryPostalCode":null,"DeliveryCity":null,"DeliveryCountryCode":null,"DeliveryMethodName":null,"DeliveryTermName":null,"DeliveryMethodCode":null,"DeliveryTermCode":null,"EuThirdParty":false,"CustomerIsPrivatePerson":false,"ReverseChargeOnConstructionServices":false,"SalesDocumentAttachments":[],"InvoiceDate":"2018-07-27T13:13:00.2738546Z","DeliveryDate":null,"TotalAmount":0.0,"TotalVatAmount":0.0,"TotalRoundings":0.0,"TotalAmountBaseCurrency":0.0,"TotalVatAmountBaseCurrency":0.0,"CustomerNumber":"107","IncludesVat":false}
"InvoiceCustomerName":"TestCustomer AB"
So the update does not go through at all.
Have contacted Visma API support for debugging since the problem is on their side.
It could be that reading and writing data to an API requires different envelop schemas.
Envelop schemas should be registered on method.
It is possible to supply a model with wrong input arguments and this might lead to unexpected errors.
for exemple
invoice = CustomerInvoiceDraft(customer_id=customer.id,
invoe_customer_name='test',
invoice_postal_code='25459',
invoice_city='Helsingborg',
customer_is_private_person=False)
We should validate that all input arguments to creating of an object acctually exists as a field on the model.
As of now in GET requests we assume that the attribute 'id' on a model is the primary key.
On some models in Visma API this is not the case. For example Account uses the account number as the key in the REST-path. /accounts/fiscal_year/number
This case also includes another case of adding path variables for special cases but that is covered in
When I try to run the first step of the visma request_access --client <client_id> the URL seems to be broken since I reach a VISMA web page complaining about an unsupported request or similar. This is for the sandbox environment.
click
doesn't get installed automatically when installing visma
via pip.
(venv) root@homedesktop:/home/sniku/workspace/teklager/teklager_se# visma
Traceback (most recent call last):
File "/home/sniku/workspace/teklager/teklager_se/venv/bin/visma", line 7, in <module>
from visma.cli import cli
File "/home/sniku/workspace/teklager/teklager_se/venv/lib/python3.6/site-packages/visma/cli.py", line 1, in <module>
import click
ModuleNotFoundError: No module named 'click'
After installing click
everything works correctly.
(venv) root@homedesktop:/home/sniku/workspace/teklager/teklager_se# pip install click
Collecting click
Downloading https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl (81kB)
100% |████████████████████████████████| 81kB 3.0MB/s
Installing collected packages: click
Successfully installed click-7.0
(venv) root@homedesktop:/home/sniku/workspace/teklager/teklager_se# visma
Usage: visma [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
get-token
request-access
In addition to normal HTTP error codes Visma eAccounting API returns a standard error response with an additional error code and description.
HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=UTF-8
{
ErrorCode: 4005,
DeveloperErrorMessage: "AuthorizationException - Authorization has been denied for this request.",
ErrorId: "1c296026-489d-434c-b7e9-70401278a086"
}
By using the ErrorCode we could parse the the response and return more helpful python excetions instead of just a VismaAPIException.
The above should raise something like this:
if error_code == 4005:
raise AuthorizationException('Authorization has been denied for this request')
Available error codes are:
This error is thrown when any data model validations are broken in the request. See the rules for each POST method on each property.
This error is thrown when you refer to a specific object that does not exist, eg. customers, suppliers or articles in a request.
This error is thrown when you refer to a specific fiscalyear that does not exist.
This error is thrown when you refer to a specific account that does not exist.
This error is thrown when you have insufficient permissions to bookeep an invoice when using supplier invoice approval flow.
This error is thrown when you try to create a object that cannot exist as a duplicate.
This error is thrown when you try to delete a object that cannot be deleted. For example, when a object has dependencies, it cannot be deleted.
This error is thrown when you try to create or update a object that contains one or more invalid properties that prevents it from being created or updated.
This error is thrown when you try to make requests towards a company that hasn't completed the startup guide in Visma eAccounting.
This error is thrown when you try to make requests towards an inactive company.
This error is thrown when you try to make unauthorized requests towards the API.
This error is thrown when you try to make requests towards an endpoint which require scopes that you are not authorized with.
This error is thrown when you try to make requests with an authorized user that does not have sufficient permissions for that specific endpoint.
This error is thrown when you try to make a request towards an endpoint with ControlDigit validation and the value is invalid.
This error is thrown when you try to make requests towards and endpoint that is not included in the authorized company's product variant.
This error is thrown when you try to more requests than allowed. Read more about this here
This error is thrown when the authenticated user does not have access to eAccounting for this company. Give the user access to eAccounting or reauthenticate with a user that have access to eAccounting to solve the issue.
This error is thrown when you encounter a error that we have not handled on our side. This can occur if there is a bug in eAccounting. These errors should be reported to us at [email protected].
This error is thrown when you make a request towards and endpoint with external service dependencies, and that dependency is not answering in time. This can occur if a service is down.
This error is thrown when you make a request with $filter parameters and the query is invalid.
This error is thrown when you make a request with DateTime properties or filtering and the provided format is wrong.
Have opened an issue with the Visma API Team about the API not accepting URL encoding of query parameters.
In the work of enabling filtering, ordering and pagination support we need to send query parameters in the Odata format to the Visma API. When doing this it seems we get an error in the parsing of the query parameters that are being URL encoded by the requests package. both problem with + and %20
{"ErrorCode":6000,"DeveloperErrorMessage":"QueryParameterException - Syntax error: character \\'\\"\\' is not valid at position 6 in \\'Id eq \\"91ff2390-968b-4ef7-877d-dd7aef616ae4\\"\\'.","ErrorId":"9cfb7373-af6e-4611-92eb-8860db42a9b3"}
Waiting for response
Get an internal server error from Visma e-Accounting API when trying to update a customer.
Thought it could be because I didn't send the Id in the PUT body, but fixing it resulted in the same error.
Contacted Visma API support for help on why we get the error.
`pip install visma
Downloading/unpacking visma
Downloading visma-0.0.3-py3-none-any.whl
Installing collected packages: visma
*** Error compiling '/tmp/pip-build-5qqko9q1/visma/visma/api.py'...
File "/tmp/pip-build-5qqko9q1/visma/visma/api.py", line 52
f'GET :: HTTP:{r.status_code}, {r.content}')
^
SyntaxError: invalid syntax
*** Error compiling '/tmp/pip-build-5qqko9q1/visma/visma/base.py'...
File "/tmp/pip-build-5qqko9q1/visma/visma/base.py", line 140
f'{self.class.name} :: {field_name} '
^
SyntaxError: invalid syntax
*** Error compiling '/tmp/pip-build-5qqko9q1/visma/visma/cli.py'...
File "/tmp/pip-build-5qqko9q1/visma/visma/cli.py", line 22
__browser.open((f'https://identity.vismaonline.com/connect/authorize'
^
SyntaxError: invalid syntax
*** Error compiling '/tmp/pip-build-5qqko9q1/visma/visma/manager.py'...
File "/tmp/pip-build-5qqko9q1/visma/visma/manager.py", line 32
f'{method} is not an allowed method on this '
^
SyntaxError: invalid syntax`
Seen with both with python2 and python3, am I missing something?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.