Skip to content

Commit

Permalink
Merge pull request #30 from veryfi/line-items
Browse files Browse the repository at this point in the history
Add support for operations with line items
  • Loading branch information
manycoding authored Mar 15, 2022
2 parents 332e77e + 1ffee34 commit 3bba1ae
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.coverage
tests/__pycache__/
veryfi/__pycache__/
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
CHANGES
=======

3.1.0
-----
* Add support for operations with line items

3.0.0
-----
* Return proper 404 and other errors
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
requests>=2.22.0
requests>=2.22.0
pydantic==1.9.0
91 changes: 91 additions & 0 deletions tests/test_line_items.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import pytest
import responses

from veryfi import *


@pytest.mark.parametrize("client_secret", [None, "s"])
@responses.activate
def test_line_items(client_secret):
mock_doc_id = 1
mock_line_item_id = 1
mock_resp = {
"line_items": [
{
"date": "",
"description": "foo",
"discount": 0.0,
"id": mock_line_item_id,
"order": 1,
"price": 0.0,
"quantity": 1.0,
"reference": "",
"sku": "",
"tax": 0.0,
"tax_rate": 0.0,
"total": 1.0,
"type": "food",
"unit_of_measure": "",
}
],
}
client = Client(client_id="v", client_secret=client_secret, username="o", api_key="c")
responses.add(
responses.GET,
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/",
json=mock_resp,
status=200,
)
assert client.get_line_items(mock_doc_id) == mock_resp

responses.add(
responses.GET,
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/{mock_line_item_id}",
json=mock_resp["line_items"][0],
status=200,
)
assert client.get_line_item(mock_doc_id, mock_line_item_id) == mock_resp["line_items"][0]

mock_resp["line_items"][0]["description"] = "bar"
responses.add(
responses.PUT,
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/{mock_line_item_id}",
json=mock_resp["line_items"][0],
status=200,
)
assert (
client.update_line_item(mock_doc_id, mock_line_item_id, {"description": "foo"})
== mock_resp["line_items"][0]
)

responses.add(
responses.DELETE,
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/{mock_line_item_id}",
json={},
status=200,
)
assert client.delete_line_item(mock_doc_id, mock_line_item_id) is None

responses.add(
responses.DELETE,
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/",
json={},
status=200,
)
assert client.delete_line_items(mock_doc_id) is None

responses.add(
responses.POST,
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/",
json=mock_resp["line_items"][0],
status=200,
)
with pytest.raises(Exception):
client.add_line_item(mock_doc_id, {"order": 1})
with pytest.raises(Exception):
client.add_line_item(mock_doc_id, {"order": 1, "description": "foo"})

assert (
client.add_line_item(mock_doc_id, {"order": 1, "description": "foo", "total": 1.0})
== mock_resp["line_items"][0]
)
67 changes: 67 additions & 0 deletions veryfi/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import requests

from veryfi.model import AddLineItem, UpdateLineItem
from veryfi.errors import VeryfiClientError


Expand Down Expand Up @@ -237,3 +238,69 @@ def update_document(self, document_id: int, **kwargs) -> Dict:
endpoint_name = f"/documents/{document_id}/"

return self._request("PUT", endpoint_name, kwargs)

def get_line_items(self, document_id):
"""
Retrieve all line items for a document.
:param document_id: ID of the document you'd like to retrieve
:return: List of line items extracted from the document
"""
endpoint_name = f"/documents/{document_id}/line-items/"
request_arguments = {}
line_items = self._request("GET", endpoint_name, request_arguments)
return line_items

def get_line_item(self, document_id, line_item_id):
"""
Retrieve a line item for existing document by ID.
:param document_id: ID of the document you'd like to retrieve
:param line_item_id: ID of the line item you'd like to retrieve
:return: Line item extracted from the document
"""
endpoint_name = f"/documents/{document_id}/line-items/{line_item_id}"
request_arguments = {}
line_items = self._request("GET", endpoint_name, request_arguments)
return line_items

def add_line_item(self, document_id: int, payload: Dict) -> Dict:
"""
Add a new line item on an existing document.
:param document_id: ID of the document you'd like to update
:param payload: line item object to add
:return: Added line item data
"""
endpoint_name = f"/documents/{document_id}/line-items/"
request_arguments = AddLineItem(**payload).dict(exclude_none=True)
return self._request("POST", endpoint_name, request_arguments)

def update_line_item(self, document_id: int, line_item_id: int, payload: Dict) -> Dict:
"""
Update an existing line item on an existing document.
:param document_id: ID of the document you'd like to update
:param line_item_id: ID of the line item you'd like to update
:param payload: line item object to update
:return: Line item data with updated fields, if fields are writable. Otherwise line item data with unchanged fields.
"""
endpoint_name = f"/documents/{document_id}/line-items/{line_item_id}"
request_arguments = UpdateLineItem(**payload).dict(exclude_none=True)
return self._request("PUT", endpoint_name, request_arguments)

def delete_line_items(self, document_id):
"""
Delete all line items on an existing document.
:param document_id: ID of the document you'd like to delete
"""
endpoint_name = f"/documents/{document_id}/line-items/"
request_arguments = {}
self._request("DELETE", endpoint_name, request_arguments)

def delete_line_item(self, document_id, line_item_id):
"""
Delete an existing line item on an existing document.
:param document_id: ID of the document you'd like to delete
:param line_item_id: ID of the line item you'd like to delete
"""
endpoint_name = f"/documents/{document_id}/line-items/{line_item_id}"
request_arguments = {}
self._request("DELETE", endpoint_name, request_arguments)
5 changes: 5 additions & 0 deletions veryfi/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class BadRequest(VeryfiClientError):
pass


class ResourceNotFound(VeryfiClientError):
pass


class UnexpectedHTTPMethod(VeryfiClientError):
pass

Expand All @@ -46,6 +50,7 @@ class InternalError(VeryfiClientError):

_error_map = {
400: BadRequest,
404: ResourceNotFound,
401: UnauthorizedAccessToken,
405: UnexpectedHTTPMethod,
409: AccessLimitReached,
Expand Down
31 changes: 31 additions & 0 deletions veryfi/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import Optional
from pydantic import BaseModel


class SharedLineItem(BaseModel):
sku: Optional[str]
category: Optional[str]
tax: Optional[float]
price: Optional[float]
unit_of_measure: Optional[str]
quantity: Optional[float]
upc: Optional[str]
tax_rate: Optional[float]
discount_rate: Optional[float]
start_date: Optional[str]
end_date: Optional[str]
hsn: Optional[str]
section: Optional[str]
weight: Optional[str]


class AddLineItem(SharedLineItem):
order: int
description: str
total: float


class UpdateLineItem(SharedLineItem):
order: Optional[int]
description: Optional[str]
total: Optional[float]

0 comments on commit 3bba1ae

Please sign in to comment.