diff --git a/pyproject.toml b/pyproject.toml index d2404c5..74b385d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "mercoa" -version = "v0.1.0" +version = "v0.1.1" description = "" authors = [] packages = [ @@ -10,10 +10,10 @@ packages = [ [tool.poetry.dependencies] python = "^3.7" -httpx = "0.23.3" -backports-cached_property = "1.0.2" pydantic = "^1.9.2" types-backports = "0.1.3" +backports-cached_property = "1.0.2" +httpx = "0.23.3" [tool.poetry.dev-dependencies] mypy = "0.971" diff --git a/src/mercoa/__init__.py b/src/mercoa/__init__.py index 041bc97..11c038d 100644 --- a/src/mercoa/__init__.py +++ b/src/mercoa/__init__.py @@ -41,7 +41,6 @@ CommentRequest, CommentResponse, CounterpartyResponse, - CreateVendorRequest, CurrencyCode, CustomId, CustomPaymentMethodRequest, @@ -114,6 +113,7 @@ Responsibilities, Rule, Rule_Approver, + SetApprover, TaxID, TransactionId, TransactionResponse, @@ -176,7 +176,6 @@ "CommentRequest", "CommentResponse", "CounterpartyResponse", - "CreateVendorRequest", "CurrencyCode", "CustomId", "CustomPaymentMethodRequest", @@ -252,6 +251,7 @@ "Rule", "Rule_Approver", "SSN", + "SetApprover", "TaxID", "TransactionId", "TransactionResponse", diff --git a/src/mercoa/resources/__init__.py b/src/mercoa/resources/__init__.py index 2ed093b..3330b79 100644 --- a/src/mercoa/resources/__init__.py +++ b/src/mercoa/resources/__init__.py @@ -70,7 +70,6 @@ CommentId, CommentRequest, CommentResponse, - CreateVendorRequest, CurrencyCode, DocumentResponse, InvoiceId, @@ -80,6 +79,7 @@ InvoiceRequest, InvoiceResponse, InvoiceStatus, + SetApprover, ) from .ocr import Attachments, EmailOcrRequest, OcrMailbox, OCRResponse from .organization import ( @@ -174,7 +174,6 @@ "CommentRequest", "CommentResponse", "CounterpartyResponse", - "CreateVendorRequest", "CurrencyCode", "CustomId", "CustomPaymentMethodRequest", @@ -249,6 +248,7 @@ "Rule", "Rule_Approver", "SSN", + "SetApprover", "TaxID", "TransactionId", "TransactionResponse", diff --git a/src/mercoa/resources/invoice/__init__.py b/src/mercoa/resources/invoice/__init__.py index 55718a9..e8e25ec 100644 --- a/src/mercoa/resources/invoice/__init__.py +++ b/src/mercoa/resources/invoice/__init__.py @@ -8,7 +8,6 @@ CommentId, CommentRequest, CommentResponse, - CreateVendorRequest, CurrencyCode, DocumentResponse, InvoiceId, @@ -18,6 +17,7 @@ InvoiceRequest, InvoiceResponse, InvoiceStatus, + SetApprover, ) __all__ = [ @@ -28,7 +28,6 @@ "CommentId", "CommentRequest", "CommentResponse", - "CreateVendorRequest", "CurrencyCode", "DocumentResponse", "InvoiceId", @@ -38,4 +37,5 @@ "InvoiceRequest", "InvoiceResponse", "InvoiceStatus", + "SetApprover", ] diff --git a/src/mercoa/resources/invoice/client.py b/src/mercoa/resources/invoice/client.py index 619926b..96d911a 100644 --- a/src/mercoa/resources/invoice/client.py +++ b/src/mercoa/resources/invoice/client.py @@ -110,7 +110,7 @@ def get_documents(self, invoice_id: InvoiceId) -> typing.List[DocumentResponse]: return pydantic.parse_obj_as(typing.List[DocumentResponse], _response_json) # type: ignore raise ApiError(status_code=_response.status_code, body=_response_json) - def approve(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> InvoiceResponse: + def approve(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> CommentResponse: _response = httpx.request( "POST", urllib.parse.urljoin(f"{self._environment.value}/", f"invoice/{invoice_id}/approve"), @@ -124,10 +124,10 @@ def approve(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> Invoice except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) if 200 <= _response.status_code < 300: - return pydantic.parse_obj_as(InvoiceResponse, _response_json) # type: ignore + return pydantic.parse_obj_as(CommentResponse, _response_json) # type: ignore raise ApiError(status_code=_response.status_code, body=_response_json) - def reject(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> InvoiceResponse: + def reject(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> CommentResponse: _response = httpx.request( "POST", urllib.parse.urljoin(f"{self._environment.value}/", f"invoice/{invoice_id}/reject"), @@ -141,7 +141,7 @@ def reject(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> InvoiceR except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) if 200 <= _response.status_code < 300: - return pydantic.parse_obj_as(InvoiceResponse, _response_json) # type: ignore + return pydantic.parse_obj_as(CommentResponse, _response_json) # type: ignore raise ApiError(status_code=_response.status_code, body=_response_json) def get_comments(self, invoice_id: InvoiceId) -> typing.List[CommentResponse]: @@ -337,7 +337,7 @@ async def get_documents(self, invoice_id: InvoiceId) -> typing.List[DocumentResp return pydantic.parse_obj_as(typing.List[DocumentResponse], _response_json) # type: ignore raise ApiError(status_code=_response.status_code, body=_response_json) - async def approve(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> InvoiceResponse: + async def approve(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> CommentResponse: async with httpx.AsyncClient() as _client: _response = await _client.request( "POST", @@ -352,10 +352,10 @@ async def approve(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> I except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) if 200 <= _response.status_code < 300: - return pydantic.parse_obj_as(InvoiceResponse, _response_json) # type: ignore + return pydantic.parse_obj_as(CommentResponse, _response_json) # type: ignore raise ApiError(status_code=_response.status_code, body=_response_json) - async def reject(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> InvoiceResponse: + async def reject(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> CommentResponse: async with httpx.AsyncClient() as _client: _response = await _client.request( "POST", @@ -370,7 +370,7 @@ async def reject(self, invoice_id: InvoiceId, *, request: ApprovalRequest) -> In except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) if 200 <= _response.status_code < 300: - return pydantic.parse_obj_as(InvoiceResponse, _response_json) # type: ignore + return pydantic.parse_obj_as(CommentResponse, _response_json) # type: ignore raise ApiError(status_code=_response.status_code, body=_response_json) async def get_comments(self, invoice_id: InvoiceId) -> typing.List[CommentResponse]: diff --git a/src/mercoa/resources/invoice/types/__init__.py b/src/mercoa/resources/invoice/types/__init__.py index 3676d6d..40c84c3 100644 --- a/src/mercoa/resources/invoice/types/__init__.py +++ b/src/mercoa/resources/invoice/types/__init__.py @@ -7,7 +7,6 @@ from .comment_id import CommentId from .comment_request import CommentRequest from .comment_response import CommentResponse -from .create_vendor_request import CreateVendorRequest from .currency_code import CurrencyCode from .document_response import DocumentResponse from .invoice_id import InvoiceId @@ -17,6 +16,7 @@ from .invoice_request import InvoiceRequest from .invoice_response import InvoiceResponse from .invoice_status import InvoiceStatus +from .set_approver import SetApprover __all__ = [ "ApprovalRequest", @@ -26,7 +26,6 @@ "CommentId", "CommentRequest", "CommentResponse", - "CreateVendorRequest", "CurrencyCode", "DocumentResponse", "InvoiceId", @@ -36,4 +35,5 @@ "InvoiceRequest", "InvoiceResponse", "InvoiceStatus", + "SetApprover", ] diff --git a/src/mercoa/resources/invoice/types/approver.py b/src/mercoa/resources/invoice/types/approver.py index 03135b4..413f1ca 100644 --- a/src/mercoa/resources/invoice/types/approver.py +++ b/src/mercoa/resources/invoice/types/approver.py @@ -3,16 +3,13 @@ import datetime as dt import typing -import pydantic - from ....core.datetime_utils import serialize_datetime -from ...entity_users.types.entity_user_id import EntityUserId from .approver_action import ApproverAction +from .set_approver import SetApprover -class Approver(pydantic.BaseModel): - user_id: EntityUserId = pydantic.Field(alias="userId") - date: dt.datetime +class Approver(SetApprover): + roles: typing.List[str] action: ApproverAction def json(self, **kwargs: typing.Any) -> str: @@ -25,5 +22,4 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True - allow_population_by_field_name = True json_encoders = {dt.datetime: serialize_datetime} diff --git a/src/mercoa/resources/invoice/types/invoice_request.py b/src/mercoa/resources/invoice/types/invoice_request.py index 6ae3f5c..e89ff7c 100644 --- a/src/mercoa/resources/invoice/types/invoice_request.py +++ b/src/mercoa/resources/invoice/types/invoice_request.py @@ -9,10 +9,10 @@ from ...entity.types.entity_id import EntityId from ...entity_users.types.entity_user_id import EntityUserId from ...payment_method.types.payment_method_id import PaymentMethodId -from .create_vendor_request import CreateVendorRequest from .currency_code import CurrencyCode from .invoice_line_item_request import InvoiceLineItemRequest from .invoice_status import InvoiceStatus +from .set_approver import SetApprover class InvoiceRequest(pydantic.BaseModel): @@ -35,20 +35,11 @@ class InvoiceRequest(pydantic.BaseModel): service_end_date: typing.Optional[dt.datetime] = pydantic.Field(alias="serviceEndDate") payer_id: typing.Optional[EntityId] = pydantic.Field(alias="payerId") payment_source_id: typing.Optional[PaymentMethodId] = pydantic.Field(alias="paymentSourceId") + approvers: typing.Optional[typing.List[SetApprover]] = pydantic.Field( + description=("Set approvers for this invoice.\n") + ) vendor_id: typing.Optional[EntityId] = pydantic.Field(alias="vendorId") payment_destination_id: typing.Optional[PaymentMethodId] = pydantic.Field(alias="paymentDestinationId") - create_vendor: typing.Optional[CreateVendorRequest] = pydantic.Field( - alias="createVendor", - description=( - "When paying to a new vendor, use the createVendor object. Mercoa will create the vendor entity and tie it to this invoice. This object is ignored when updating an invoice.\n" - ), - ) - update_vendor: typing.Optional[CreateVendorRequest] = pydantic.Field( - alias="updateVendor", - description=( - "When paying to an existing vendor with an incomplete profile, use the updateVendor object. Mercoa will update the vendor entity tied to this invoice. This object is ignored if the vendor already has already been created with complete information and when creating a new invoice.\n" - ), - ) line_items: typing.Optional[typing.List[InvoiceLineItemRequest]] = pydantic.Field(alias="lineItems") metadata: typing.Optional[typing.Dict[str, str]] = pydantic.Field( description=( diff --git a/src/mercoa/resources/invoice/types/invoice_response.py b/src/mercoa/resources/invoice/types/invoice_response.py index f108223..4c61e7e 100644 --- a/src/mercoa/resources/invoice/types/invoice_response.py +++ b/src/mercoa/resources/invoice/types/invoice_response.py @@ -6,6 +6,7 @@ import pydantic from ....core.datetime_utils import serialize_datetime +from ...entity.types.approval_policy_response import ApprovalPolicyResponse from ...entity.types.entity_id import EntityId from ...entity.types.entity_response import EntityResponse from ...entity_users.types.entity_user_response import EntityUserResponse @@ -53,6 +54,7 @@ class InvoiceResponse(pydantic.BaseModel): transactions: typing.Optional[typing.List[TransactionResponse]] line_items: typing.Optional[typing.List[InvoiceLineItemResponse]] = pydantic.Field(alias="lineItems") approvers: typing.List[Approver] + approval_policy: typing.List[ApprovalPolicyResponse] = pydantic.Field(alias="approvalPolicy") metadata: typing.Dict[str, str] = pydantic.Field(description=("Metadata associated with this invoice.\n")) created_by: typing.Optional[EntityUserResponse] = pydantic.Field( alias="createdBy", description=("Entity user who created this invoice.\n") diff --git a/src/mercoa/resources/invoice/types/create_vendor_request.py b/src/mercoa/resources/invoice/types/set_approver.py similarity index 70% rename from src/mercoa/resources/invoice/types/create_vendor_request.py rename to src/mercoa/resources/invoice/types/set_approver.py index 1c6e2a4..b21952a 100644 --- a/src/mercoa/resources/invoice/types/create_vendor_request.py +++ b/src/mercoa/resources/invoice/types/set_approver.py @@ -6,13 +6,12 @@ import pydantic from ....core.datetime_utils import serialize_datetime -from ...entity.types.entity_request import EntityRequest -from ...payment_method.types.payment_method_request import PaymentMethodRequest +from ...entity_users.types.entity_user_id import EntityUserId -class CreateVendorRequest(pydantic.BaseModel): - vendor: EntityRequest - payment_method: typing.Optional[PaymentMethodRequest] = pydantic.Field(alias="paymentMethod") +class SetApprover(pydantic.BaseModel): + user_id: EntityUserId = pydantic.Field(alias="userId") + date: dt.datetime def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = {"by_alias": True, "exclude_unset": True, **kwargs} diff --git a/src/mercoa/resources/ocr/client.py b/src/mercoa/resources/ocr/client.py index 2353f83..3b1da3f 100644 --- a/src/mercoa/resources/ocr/client.py +++ b/src/mercoa/resources/ocr/client.py @@ -12,7 +12,6 @@ from ...core.remove_none_from_headers import remove_none_from_headers from ...environment import MercoaEnvironment from ..organization.types.organization_id import OrganizationId -from .types.email_ocr_request import EmailOcrRequest from .types.ocr_response import OCRResponse @@ -40,12 +39,12 @@ def ocr(self, *, mime_type: str, image: str) -> OCRResponse: return pydantic.parse_obj_as(OCRResponse, _response_json) # type: ignore raise ApiError(status_code=_response.status_code, body=_response_json) - def email_inbox(self, *, org: OrganizationId, items: typing.List[EmailOcrRequest]) -> None: + def email_inbox(self, *, org: OrganizationId, request: typing.Any) -> None: _response = httpx.request( "POST", urllib.parse.urljoin(f"{self._environment.value}/", "emailOcr"), params={"org": org}, - json=jsonable_encoder({"items": items}), + json=jsonable_encoder(request), headers=remove_none_from_headers( {"Authorization": f"Bearer {self._token}" if self._token is not None else None} ), @@ -84,13 +83,13 @@ async def ocr(self, *, mime_type: str, image: str) -> OCRResponse: return pydantic.parse_obj_as(OCRResponse, _response_json) # type: ignore raise ApiError(status_code=_response.status_code, body=_response_json) - async def email_inbox(self, *, org: OrganizationId, items: typing.List[EmailOcrRequest]) -> None: + async def email_inbox(self, *, org: OrganizationId, request: typing.Any) -> None: async with httpx.AsyncClient() as _client: _response = await _client.request( "POST", urllib.parse.urljoin(f"{self._environment.value}/", "emailOcr"), params={"org": org}, - json=jsonable_encoder({"items": items}), + json=jsonable_encoder(request), headers=remove_none_from_headers( {"Authorization": f"Bearer {self._token}" if self._token is not None else None} ),