GithubHelp home page GithubHelp logo

pwitab / visma Goto Github PK

View Code? Open in Web Editor NEW
7.0 3.0 4.0 195 KB

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

Python 100.00%
orm visma accounting integration api-client

visma's Issues

Conflict creating projects

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.

Handle Enveloped responses

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.

Support extra REST-paths on model

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)

Limit query result

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.

Updating CustomerInvoiceDraft not working

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.

Validate input arguments.

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.

Support any field to be primary key

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

Broken URL

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

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

Make Visma eAccounting API error codes into python native exceptions

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:

DataModelValidationError - 2000

This error is thrown when any data model validations are broken in the request. See the rules for each POST method on each property.

ResourceNotFound - 3000

This error is thrown when you refer to a specific object that does not exist, eg. customers, suppliers or articles in a request.

FiscalYearDoesNotExist - 3001

This error is thrown when you refer to a specific fiscalyear that does not exist.

AccountNotFound - 3002

This error is thrown when you refer to a specific account that does not exist.

InsufficientApprovalPermission - 3003

This error is thrown when you have insufficient permissions to bookeep an invoice when using supplier invoice approval flow.

Confilct - 4000

This error is thrown when you try to create a object that cannot exist as a duplicate.

CanNotBeDeleted - 4001

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.

CanNotBeCreatedOrUpdated - 4002

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.

StartupGuideNotCompleted - 4003

This error is thrown when you try to make requests towards a company that hasn't completed the startup guide in Visma eAccounting.

CompanyNotActive - 4004

This error is thrown when you try to make requests towards an inactive company.

NotAuthorized - 4005

This error is thrown when you try to make unauthorized requests towards the API.

NoAccessToScopes - 4006

This error is thrown when you try to make requests towards an endpoint which require scopes that you are not authorized with.

PermissionDenied - 4007

This error is thrown when you try to make requests with an authorized user that does not have sufficient permissions for that specific endpoint.

InvalidControlDigit - 4008

This error is thrown when you try to make a request towards an endpoint with ControlDigit validation and the value is invalid.

NoAccessDueToProductVariant - 4009

This error is thrown when you try to make requests towards and endpoint that is not included in the authorized company's product variant.

TooManyRequests - 4010

This error is thrown when you try to more requests than allowed. Read more about this here

NoAccessForAuthenticatedUser - 4011

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.

InternalServerError - 5000

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].

CantReachSystemDependendency - 5001

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.

QueryParameterError - 6000

This error is thrown when you make a request with $filter parameters and the query is invalid.

InvalidInput - 6001

This error is thrown when you make a request with DateTime properties or filtering and the provided format is wrong.

URL encoding of query parameters failing.

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

GET: 'https://eaccountingapi-sandbox.test.vismaonline.com/v2/customers?$filter=Id%20eq%20%2291ff2390-968b-4ef7-877d-dd7aef616ae4%22'

{"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

Can't update customer

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 broken?

`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?

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.