Skip to content

Commit

Permalink
add InstalmentLinkOpen
Browse files Browse the repository at this point in the history
  • Loading branch information
hwjeremy committed Dec 15, 2020
1 parent 07e1df1 commit 409e2b5
Show file tree
Hide file tree
Showing 13 changed files with 286 additions and 18 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.4
0.0.5
5 changes: 4 additions & 1 deletion procuret/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from procuret.session import Session, Lifecycle, Perspective
from procuret.instalment_link import InstalmentLink, CommunicationOption
from procuret.ancillary.communication_option import CommunicationOption
from procuret.instalment_link import InstalmentLink, InstalmentLinkOpen
from procuret.instalment_link import InstalmentLinkOrderBy
from procuret.data.order import Order
from procuret.ancillary.entity_headline import EntityHeadline
from procuret import errors
3 changes: 2 additions & 1 deletion procuret/data/codable.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Callable
from enum import Enum
from decimal import Decimal
from procuret.data.decodable import Decodable

T = TypeVar('T', bound='Codable')
K = TypeVar('K', bound='CodableType')
Expand Down Expand Up @@ -93,7 +94,7 @@ def decode(self, data: Any) -> Any:
raise


class Codable:
class Codable(Decodable):

coding_map: Dict[str, CodingDefinition] = NotImplemented

Expand Down
57 changes: 57 additions & 0 deletions procuret/data/decodable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
Procuret Python
Decodable Module
author: [email protected]
"""
from json import loads
from typing import Any, Optional, TypeVar, Type, List

T = TypeVar('T', bound='Decodable')


class Decodable:
"""Abstract protocol defining an interface for decodable classes"""

@classmethod
def decode(self, data: Any) -> T:
"""Return a JSON-serialisable form of the object"""
raise NotImplementedError

@classmethod
def optionally_decode(cls: Type[T], data: Optional[Any]) -> Optional[T]:
"""Optionally return a decoded object from serialised data"""
if data is None:
return None
return cls.decode(data)

@classmethod
def deserialise(cls: Type[T], serial: str) -> T:
"""Return a JSON string representation of the object"""
return cls.decode(loads(serial))

@classmethod
def optionally_deserialise(
cls: Type[T],
serial: Optional[str]
) -> Optional[T]:
if serial is None:
return None
return cls.deserialise(serial)

@classmethod
def decode_many(cls: Type[T], data: Any) -> List[T]:
"""Return list of decoded instances of an object"""
return [cls.decode(d) for d in data]

@classmethod
def optionally_decode_many(
cls: Type[T],
data: Optional[Any],
default_to_empty_list: bool = False
) -> Optional[List[T]]:
"""Optionally return a list of decoded objects"""
if data is None and default_to_empty_list is True:
return list()
if data is None:
return None
return cls.decode_many(data)
12 changes: 12 additions & 0 deletions procuret/data/order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
Procuret Python
Order Enumeration Module
author: [email protected]
"""
from enum import Enum


class Order(Enum):

ASCENDING = 'ascending'
DESCENDING = 'descending'
5 changes: 3 additions & 2 deletions procuret/http/api_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,15 @@ def make(
session=session
)

encoded_data: Optional[bytes] = None
if data is not None:
data = json.dumps(data).encode('utf-8')
encoded_data = json.dumps(data).encode('utf-8')
headers['Content-Type'] = 'application/json'

request = Request(
url=url,
method=method.value,
data=data,
data=encoded_data,
headers=headers
)

Expand Down
17 changes: 16 additions & 1 deletion procuret/http/query_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ def __init__(
self._key = key
self._value = value

self._url_representation = self._represent(value)

return

key = property(lambda s: s._key)

def __str__(self) -> str:
return self._key + '=' + str(self._value)
return self._key + '=' + self._url_representation

@classmethod
def remove_targets_with(
Expand All @@ -43,3 +45,16 @@ def remove_targets_with(
continue

return targets

@staticmethod
def _represent(value: Any) -> str:

if isinstance(value, str):
return value

if isinstance(value, bool):
if value is True:
return 'true'
return 'false'

return str(value)
5 changes: 5 additions & 0 deletions procuret/instalment_link/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from procuret.instalment_link.instalment_link import InstalmentLink
from procuret.instalment_link.open import InstalmentLinkOpen
from procuret.instalment_link.instalment_link import (
OrderBy as InstalmentLinkOrderBy
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,70 @@
author: [email protected]
"""
from procuret.ancillary.communication_option import CommunicationOption
from typing import TypeVar, Type, Union
from typing import TypeVar, Type, Union, List
from procuret.data.codable import Codable, CodingDefinition as CD
from procuret.ancillary.entity_headline import EntityHeadline
from procuret.errors.type_error import ProcuretTypeError
from procuret.http.api_request import ApiRequest, HTTPMethod
from procuret.errors.inconsistent import InconsistentState
from procuret.session import Session
from decimal import Decimal
from enum import Enum
from procuret.data.order import Order
from procuret.http.query_parameters import QueryParameter, QueryParameters
from typing import Optional
from procuret.instalment_link.open import InstalmentLinkOpen

T = TypeVar('T', bound='InstalmentLink')

Self = TypeVar('Self', bound='InstalmentLink')


class OrderBy(Enum):
CREATED = 'created'


class InstalmentLink(Codable):

PATH = '/instalment-link'
path = '/instalment-link'
list_path = path + '/list'

_LINK_TEMPLATE = 'https://procuret.com/business/signup?supplier_id={entity\
_id}&presented_invoice_id={invoice_id}&presented_invoice_amount={invoice_amoun\
t}'

coding_map = {
'public_id': CD(str),
'supplier': CD(EntityHeadline),
'invoice_amount': CD(Decimal),
'invitee_email': CD(str),
'invoice_identifier': CD(str),
'opens': CD(InstalmentLinkOpen, array=True)
}

def __init__(
self,
public_id: str,
supplier: EntityHeadline,
invitee_email: str,
invoice_amount: Decimal,
invoice_identifier: str
invoice_identifier: str,
opens: List[InstalmentLinkOpen]
) -> None:

self._supplier = supplier
self._public_id = public_id
self._invitee_email = invitee_email
self._invoice_amount = invoice_amount
self._invoice_identifier = invoice_identifier
self._opens = opens

return

public_id = property(lambda s: s._public_id)
invitee_email = property(lambda s: s._invitee_email)
invoice_amount = property(lambda s: s._invoice_amount)
invoice_identifier = property(lambda s: s._invoice_identifier)
opens = property(lambda s: s._opens)

url = property(lambda s: s._LINK_TEMPLATE.format(
entity_id=str(s._supplier.entity_id),
Expand All @@ -58,14 +77,14 @@ def __init__(

@classmethod
def create(
cls: Type[T],
cls: Type[Self],
supplier: Union[int, EntityHeadline],
invoice_amount: Decimal,
invitee_email: str,
invoice_identifier: str,
communication: CommunicationOption,
session: Session
) -> T:
) -> Self:

def infer_supplier_id(x: Union[int, EntityHeadline]) -> int:
if isinstance(x, int):
Expand Down Expand Up @@ -113,7 +132,7 @@ def infer_communication(y: CommunicationOption) -> bool:
}

result = ApiRequest.make(
path=cls.PATH,
path=cls.path,
method=HTTPMethod.POST,
data=data,
session=session,
Expand All @@ -124,3 +143,77 @@ def infer_communication(y: CommunicationOption) -> bool:
raise InconsistentState

return cls.decode(result)

@classmethod
def retrieve(
cls: Type[Self],
public_id: str,
session: Session
) -> Optional[Self]:

if not isinstance(public_id, str):
raise TypeError('public_id must be a string')

result = cls.retrieve_many(
public_id=public_id,
session=session
)

if len(result) < 1:
return None

return result[0]

@classmethod
def retrieve_many(
cls: Type[Self],
session: Session,
public_id: Optional[str] = None,
limit: int = 20,
offset: int = 0,
order: Order = Order.ASCENDING,
order_by: OrderBy = OrderBy.CREATED,
opened: Optional[bool] = None
) -> List[Self]:

if not isinstance(limit, int):
raise TypeError('limit must be an integer')

if not isinstance(offset, int):
raise TypeError('offset must be an integer')

if not isinstance(order, Order):
raise TypeError('order must be of type Order')

if not isinstance(order_by, OrderBy):
raise TypeError('order must be of type InstalmentLink.OrderBy')

if not isinstance(session, Session):
raise TypeError('session must be of type Session')

parameters = [
QueryParameter('limit', limit),
QueryParameter('offset', offset),
QueryParameter('order', order.value),
QueryParameter('order_by', order_by.value),
]

if opened is not None:
if not isinstance(opened, bool):
raise TypeError('If supplied, opened must be a bool')
parameters.append(QueryParameter('opened', opened))

if public_id is not None:
if not isinstance(public_id, str):
raise TypeError('If supplied, public_id must be str')
parameters.append(QueryParameter('public_id', public_id))

result = ApiRequest.make(
path=cls.list_path,
method=HTTPMethod.GET,
data=None,
session=session,
query_parameters=QueryParameters(parameters)
)

return cls.optionally_decode_many(result, default_to_empty_list=True)
59 changes: 59 additions & 0 deletions procuret/instalment_link/open.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
Procuret Python
Instalment Link Open Module
author: [email protected]
"""
from procuret.time.time import ProcuretTime
from procuret.data.codable import Codable, CodingDefinition as CD
from typing import TypeVar, Type
from procuret.http.api_request import ApiRequest, HTTPMethod
from procuret.session import Session
from procuret.errors.type_error import ProcuretTypeError

Self = TypeVar('Self', bound='InstalmentLinkOpen')


class InstalmentLinkOpen(Codable):

path = '/instalment-link/open'

coding_map = {
'sequence': CD(int),
'created': CD(ProcuretTime)
}

def __init__(
self,
sequence: int,
created: ProcuretTime
) -> None:

self._sequence = sequence
self._created = created

return

sequence = property(lambda s: s._sequence)
created = property(lambda s: s._created)

@classmethod
def create(
cls: Type[Self],
link_id: str,
session: Session
) -> None:

if not isinstance(link_id, str):
raise ProcuretTypeError('str', link_id, 'link_id')

if not isinstance(session, Session):
raise ProcuretTypeError('Session', session, 'session')

result = ApiRequest.make(
path=cls.path,
method=HTTPMethod.POST,
data={'link_id': link_id},
session=session
)

return None
Loading

0 comments on commit 409e2b5

Please sign in to comment.