Skip to content

Commit 1ffee34

Browse files
author
Hoanh An
committed
Add support for operations with line items
Add line items Update doc Clean up pprint Add more fields Add news
1 parent 991c115 commit 1ffee34

7 files changed

Lines changed: 203 additions & 1 deletion

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.coverage
2+
tests/__pycache__/
3+
veryfi/__pycache__/

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
CHANGES
22
=======
33

4+
3.1.0
5+
-----
6+
* Add support for operations with line items
7+
48
3.0.0
59
-----
610
* Use v8 by default, lower timeout

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
requests>=2.22.0
1+
requests>=2.22.0
2+
pydantic==1.9.0

tests/test_line_items.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import pytest
2+
import responses
3+
4+
from veryfi import *
5+
6+
7+
@pytest.mark.parametrize("client_secret", [None, "s"])
8+
@responses.activate
9+
def test_line_items(client_secret):
10+
mock_doc_id = 1
11+
mock_line_item_id = 1
12+
mock_resp = {
13+
"line_items": [
14+
{
15+
"date": "",
16+
"description": "foo",
17+
"discount": 0.0,
18+
"id": mock_line_item_id,
19+
"order": 1,
20+
"price": 0.0,
21+
"quantity": 1.0,
22+
"reference": "",
23+
"sku": "",
24+
"tax": 0.0,
25+
"tax_rate": 0.0,
26+
"total": 1.0,
27+
"type": "food",
28+
"unit_of_measure": "",
29+
}
30+
],
31+
}
32+
client = Client(client_id="v", client_secret=client_secret, username="o", api_key="c")
33+
responses.add(
34+
responses.GET,
35+
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/",
36+
json=mock_resp,
37+
status=200,
38+
)
39+
assert client.get_line_items(mock_doc_id) == mock_resp
40+
41+
responses.add(
42+
responses.GET,
43+
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/{mock_line_item_id}",
44+
json=mock_resp["line_items"][0],
45+
status=200,
46+
)
47+
assert client.get_line_item(mock_doc_id, mock_line_item_id) == mock_resp["line_items"][0]
48+
49+
mock_resp["line_items"][0]["description"] = "bar"
50+
responses.add(
51+
responses.PUT,
52+
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/{mock_line_item_id}",
53+
json=mock_resp["line_items"][0],
54+
status=200,
55+
)
56+
assert (
57+
client.update_line_item(mock_doc_id, mock_line_item_id, {"description": "foo"})
58+
== mock_resp["line_items"][0]
59+
)
60+
61+
responses.add(
62+
responses.DELETE,
63+
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/{mock_line_item_id}",
64+
json={},
65+
status=200,
66+
)
67+
assert client.delete_line_item(mock_doc_id, mock_line_item_id) is None
68+
69+
responses.add(
70+
responses.DELETE,
71+
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/",
72+
json={},
73+
status=200,
74+
)
75+
assert client.delete_line_items(mock_doc_id) is None
76+
77+
responses.add(
78+
responses.POST,
79+
f"{client.versioned_url}/partner/documents/{mock_doc_id}/line-items/",
80+
json=mock_resp["line_items"][0],
81+
status=200,
82+
)
83+
with pytest.raises(Exception):
84+
client.add_line_item(mock_doc_id, {"order": 1})
85+
with pytest.raises(Exception):
86+
client.add_line_item(mock_doc_id, {"order": 1, "description": "foo"})
87+
88+
assert (
89+
client.add_line_item(mock_doc_id, {"order": 1, "description": "foo", "total": 1.0})
90+
== mock_resp["line_items"][0]
91+
)

veryfi/client.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import requests
1010

11+
from veryfi.model import AddLineItem, UpdateLineItem
1112
from veryfi.errors import VeryfiClientError
1213

1314

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

239240
return self._request("PUT", endpoint_name, kwargs)
241+
242+
def get_line_items(self, document_id):
243+
"""
244+
Retrieve all line items for a document.
245+
:param document_id: ID of the document you'd like to retrieve
246+
:return: List of line items extracted from the document
247+
"""
248+
endpoint_name = f"/documents/{document_id}/line-items/"
249+
request_arguments = {}
250+
line_items = self._request("GET", endpoint_name, request_arguments)
251+
return line_items
252+
253+
def get_line_item(self, document_id, line_item_id):
254+
"""
255+
Retrieve a line item for existing document by ID.
256+
:param document_id: ID of the document you'd like to retrieve
257+
:param line_item_id: ID of the line item you'd like to retrieve
258+
:return: Line item extracted from the document
259+
"""
260+
endpoint_name = f"/documents/{document_id}/line-items/{line_item_id}"
261+
request_arguments = {}
262+
line_items = self._request("GET", endpoint_name, request_arguments)
263+
return line_items
264+
265+
def add_line_item(self, document_id: int, payload: Dict) -> Dict:
266+
"""
267+
Add a new line item on an existing document.
268+
:param document_id: ID of the document you'd like to update
269+
:param payload: line item object to add
270+
:return: Added line item data
271+
"""
272+
endpoint_name = f"/documents/{document_id}/line-items/"
273+
request_arguments = AddLineItem(**payload).dict(exclude_none=True)
274+
return self._request("POST", endpoint_name, request_arguments)
275+
276+
def update_line_item(self, document_id: int, line_item_id: int, payload: Dict) -> Dict:
277+
"""
278+
Update an existing line item on an existing document.
279+
:param document_id: ID of the document you'd like to update
280+
:param line_item_id: ID of the line item you'd like to update
281+
:param payload: line item object to update
282+
283+
:return: Line item data with updated fields, if fields are writable. Otherwise line item data with unchanged fields.
284+
"""
285+
endpoint_name = f"/documents/{document_id}/line-items/{line_item_id}"
286+
request_arguments = UpdateLineItem(**payload).dict(exclude_none=True)
287+
return self._request("PUT", endpoint_name, request_arguments)
288+
289+
def delete_line_items(self, document_id):
290+
"""
291+
Delete all line items on an existing document.
292+
:param document_id: ID of the document you'd like to delete
293+
"""
294+
endpoint_name = f"/documents/{document_id}/line-items/"
295+
request_arguments = {}
296+
self._request("DELETE", endpoint_name, request_arguments)
297+
298+
def delete_line_item(self, document_id, line_item_id):
299+
"""
300+
Delete an existing line item on an existing document.
301+
:param document_id: ID of the document you'd like to delete
302+
:param line_item_id: ID of the line item you'd like to delete
303+
"""
304+
endpoint_name = f"/documents/{document_id}/line-items/{line_item_id}"
305+
request_arguments = {}
306+
self._request("DELETE", endpoint_name, request_arguments)

veryfi/errors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ class BadRequest(VeryfiClientError):
4242
pass
4343

4444

45+
class ResourceNotFound(VeryfiClientError):
46+
pass
47+
48+
4549
class UnexpectedHTTPMethod(VeryfiClientError):
4650
pass
4751

@@ -56,6 +60,7 @@ class InternalError(VeryfiClientError):
5660

5761
_error_map = {
5862
400: BadRequest,
63+
404: ResourceNotFound,
5964
401: UnauthorizedAccessToken,
6065
405: UnexpectedHTTPMethod,
6166
409: AccessLimitReached,

veryfi/model.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Optional
2+
from pydantic import BaseModel
3+
4+
5+
class SharedLineItem(BaseModel):
6+
sku: Optional[str]
7+
category: Optional[str]
8+
tax: Optional[float]
9+
price: Optional[float]
10+
unit_of_measure: Optional[str]
11+
quantity: Optional[float]
12+
upc: Optional[str]
13+
tax_rate: Optional[float]
14+
discount_rate: Optional[float]
15+
start_date: Optional[str]
16+
end_date: Optional[str]
17+
hsn: Optional[str]
18+
section: Optional[str]
19+
weight: Optional[str]
20+
21+
22+
class AddLineItem(SharedLineItem):
23+
order: int
24+
description: str
25+
total: float
26+
27+
28+
class UpdateLineItem(SharedLineItem):
29+
order: Optional[int]
30+
description: Optional[str]
31+
total: Optional[float]

0 commit comments

Comments
 (0)