# This file was auto-generated by Fern from our API Definition.

import datetime as dt
import typing
from json.decoder import JSONDecodeError

from ..commons.errors.bad_request import BadRequest
from ..commons.errors.conflict import Conflict
from ..commons.errors.forbidden import Forbidden
from ..commons.errors.internal_server_error import InternalServerError
from ..commons.errors.not_found import NotFound
from ..commons.errors.unauthorized import Unauthorized
from ..commons.errors.unimplemented import Unimplemented
from ..commons.types.order_direction import OrderDirection
from ..core.api_error import ApiError
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.datetime_utils import serialize_datetime
from ..core.jsonable_encoder import jsonable_encoder
from ..core.pydantic_utilities import pydantic_v1
from ..core.request_options import RequestOptions
from ..entity_types.types.entity_id import EntityId
from ..entity_types.types.entity_user_id import EntityUserId
from ..invoice_types.types.approval_slot_assignment import ApprovalSlotAssignment
from ..invoice_types.types.approver_action import ApproverAction
from ..invoice_types.types.find_invoice_response import FindInvoiceResponse
from ..invoice_types.types.invoice_failure_type import InvoiceFailureType
from ..invoice_types.types.invoice_id import InvoiceId
from ..invoice_types.types.invoice_line_item_request import InvoiceLineItemRequest
from ..invoice_types.types.invoice_metadata_filter import InvoiceMetadataFilter
from ..invoice_types.types.invoice_order_by_field import InvoiceOrderByField
from ..invoice_types.types.invoice_response import InvoiceResponse
from ..invoice_types.types.invoice_status import InvoiceStatus
from ..invoice_types.types.payment_destination_options import PaymentDestinationOptions
from ..payment_method_types.types.currency_code import CurrencyCode
from ..payment_method_types.types.payment_method_id import PaymentMethodId
from .approval.client import ApprovalClient, AsyncApprovalClient
from .comment.client import AsyncCommentClient, CommentClient
from .document.client import AsyncDocumentClient, DocumentClient
from .payment_links.client import AsyncPaymentLinksClient, PaymentLinksClient

# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)


class InvoiceClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._client_wrapper = client_wrapper
        self.approval = ApprovalClient(client_wrapper=self._client_wrapper)
        self.comment = CommentClient(client_wrapper=self._client_wrapper)
        self.document = DocumentClient(client_wrapper=self._client_wrapper)
        self.payment_links = PaymentLinksClient(client_wrapper=self._client_wrapper)

    def find(
        self,
        *,
        entity_id: typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]] = None,
        start_date: typing.Optional[dt.datetime] = None,
        end_date: typing.Optional[dt.datetime] = None,
        order_by: typing.Optional[InvoiceOrderByField] = None,
        order_direction: typing.Optional[OrderDirection] = None,
        limit: typing.Optional[int] = None,
        starting_after: typing.Optional[InvoiceId] = None,
        search: typing.Optional[str] = None,
        metadata: typing.Optional[typing.Union[InvoiceMetadataFilter, typing.Sequence[InvoiceMetadataFilter]]] = None,
        payer_id: typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]] = None,
        vendor_id: typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]] = None,
        approver_id: typing.Optional[typing.Union[EntityUserId, typing.Sequence[EntityUserId]]] = None,
        approver_action: typing.Optional[typing.Union[ApproverAction, typing.Sequence[ApproverAction]]] = None,
        invoice_id: typing.Optional[typing.Union[InvoiceId, typing.Sequence[InvoiceId]]] = None,
        status: typing.Optional[typing.Union[InvoiceStatus, typing.Sequence[InvoiceStatus]]] = None,
        include_fees: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> FindInvoiceResponse:
        """
        Search invoices for all entities in the organization

        Parameters
        ----------
        entity_id : typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]]
            Filter invoices by the ID of the entity that created the invoice.

        start_date : typing.Optional[dt.datetime]
            Start date for invoice created on date filter.

        end_date : typing.Optional[dt.datetime]
            End date for invoice created date filter.

        order_by : typing.Optional[InvoiceOrderByField]
            Field to order invoices by. Defaults to CREATED_AT.

        order_direction : typing.Optional[OrderDirection]
            Direction to order invoices by. Defaults to asc.

        limit : typing.Optional[int]
            Number of invoices to return. Limit can range between 1 and 100, and the default is 10.

        starting_after : typing.Optional[InvoiceId]
            The ID of the invoice to start after. If not provided, the first page of invoices will be returned.

        search : typing.Optional[str]
            Find invoices by vendor name, invoice number, or amount. Partial matches are supported.

        metadata : typing.Optional[typing.Union[InvoiceMetadataFilter, typing.Sequence[InvoiceMetadataFilter]]]
            Filter invoices by metadata. Each filter will be applied as an AND condition. Duplicate keys will be ignored.

        payer_id : typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]]
            Filter invoices by payer ID.

        vendor_id : typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]]
            Filter invoices by vendor ID.

        approver_id : typing.Optional[typing.Union[EntityUserId, typing.Sequence[EntityUserId]]]
            Filter invoices by assigned approver user ID.

        approver_action : typing.Optional[typing.Union[ApproverAction, typing.Sequence[ApproverAction]]]
            Filter invoices by approver action. Needs to be used with approverId. For example, if you want to find all invoices that have been approved by a specific user, you would use approverId and approverAction=APPROVE.

        invoice_id : typing.Optional[typing.Union[InvoiceId, typing.Sequence[InvoiceId]]]
            Filter invoices by invoice ID.

        status : typing.Optional[typing.Union[InvoiceStatus, typing.Sequence[InvoiceStatus]]]
            Invoice status to filter on

        include_fees : typing.Optional[bool]
            DEPRECATED. Fees are now included by default in the response.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        FindInvoiceResponse

        Examples
        --------
        from mercoa.client import Mercoa

        client = Mercoa(
            token="YOUR_TOKEN",
        )
        client.invoice.find(
            entity_id="ent_8545a84e-a45f-41bf-bdf1-33b42a55812c",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "invoices",
            method="GET",
            params={
                "entityId": entity_id,
                "startDate": serialize_datetime(start_date) if start_date is not None else None,
                "endDate": serialize_datetime(end_date) if end_date is not None else None,
                "orderBy": order_by,
                "orderDirection": order_direction,
                "limit": limit,
                "startingAfter": starting_after,
                "search": search,
                "metadata": jsonable_encoder(metadata),
                "payerId": payer_id,
                "vendorId": vendor_id,
                "approverId": approver_id,
                "approverAction": approver_action,
                "invoiceId": invoice_id,
                "status": status,
                "includeFees": include_fees,
            },
            request_options=request_options,
        )
        try:
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        if 200 <= _response.status_code < 300:
            return pydantic_v1.parse_obj_as(FindInvoiceResponse, _response_json)  # type: ignore
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "NotFound":
                raise NotFound(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Conflict":
                raise Conflict(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def create(
        self,
        *,
        creator_entity_id: EntityId,
        status: typing.Optional[InvoiceStatus] = OMIT,
        amount: typing.Optional[float] = OMIT,
        currency: typing.Optional[CurrencyCode] = OMIT,
        invoice_date: typing.Optional[dt.datetime] = OMIT,
        deduction_date: typing.Optional[dt.datetime] = OMIT,
        settlement_date: typing.Optional[dt.datetime] = OMIT,
        due_date: typing.Optional[dt.datetime] = OMIT,
        invoice_number: typing.Optional[str] = OMIT,
        note_to_self: typing.Optional[str] = OMIT,
        service_start_date: typing.Optional[dt.datetime] = OMIT,
        service_end_date: typing.Optional[dt.datetime] = OMIT,
        payer_id: typing.Optional[EntityId] = OMIT,
        payment_source_id: typing.Optional[PaymentMethodId] = OMIT,
        vendor_id: typing.Optional[EntityId] = OMIT,
        payment_destination_id: typing.Optional[PaymentMethodId] = OMIT,
        payment_destination_options: typing.Optional[PaymentDestinationOptions] = OMIT,
        approvers: typing.Optional[typing.Sequence[ApprovalSlotAssignment]] = OMIT,
        line_items: typing.Optional[typing.Sequence[InvoiceLineItemRequest]] = OMIT,
        metadata: typing.Optional[typing.Dict[str, str]] = OMIT,
        foreign_id: typing.Optional[str] = OMIT,
        document: typing.Optional[str] = OMIT,
        uploaded_image: typing.Optional[str] = OMIT,
        creator_user_id: typing.Optional[EntityUserId] = OMIT,
        failure_type: typing.Optional[InvoiceFailureType] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> InvoiceResponse:
        """
        Parameters
        ----------
        creator_entity_id : EntityId
            ID of entity who created this invoice.

        status : typing.Optional[InvoiceStatus]

        amount : typing.Optional[float]
            Total amount of invoice in major units. If the entered amount has more decimal places than the currency supports, trailing decimals will be truncated.

        currency : typing.Optional[CurrencyCode]
            Currency code for the amount. Defaults to USD.

        invoice_date : typing.Optional[dt.datetime]
            Date the invoice was issued.

        deduction_date : typing.Optional[dt.datetime]
            Date when funds are scheduled to be deducted from payer's account.

        settlement_date : typing.Optional[dt.datetime]
            Date of funds settlement.

        due_date : typing.Optional[dt.datetime]
            Due date of invoice.

        invoice_number : typing.Optional[str]

        note_to_self : typing.Optional[str]
            Note to self or memo on invoice.

        service_start_date : typing.Optional[dt.datetime]

        service_end_date : typing.Optional[dt.datetime]

        payer_id : typing.Optional[EntityId]

        payment_source_id : typing.Optional[PaymentMethodId]
            ID of payment source for this invoice. If not provided, will attempt to use the default payment source for the payer when creating an invoice if a default payment source exists for the payer.

        vendor_id : typing.Optional[EntityId]

        payment_destination_id : typing.Optional[PaymentMethodId]
            ID of payment destination for this invoice. If not provided, will attempt to use the default payment destination for the vendor when creating an invoice if a default payment destination exists for the vendor.

        payment_destination_options : typing.Optional[PaymentDestinationOptions]
            Options for the payment destination. Depending on the payment destination, this may include things such as check delivery method.

        approvers : typing.Optional[typing.Sequence[ApprovalSlotAssignment]]
            Set approvers for this invoice.

        line_items : typing.Optional[typing.Sequence[InvoiceLineItemRequest]]

        metadata : typing.Optional[typing.Dict[str, str]]
            Metadata associated with this invoice. You can specify up to 10 keys, with key names up to 40 characters long and values up to 200 characters long.

        foreign_id : typing.Optional[str]
            The ID used to identify this invoice in your system. This ID must be unique within each creatorEntity in your system, e.g. two invoices with the same creatorEntity may not have the same foreign ID.

        document : typing.Optional[str]
            Base64 encoded image or PDF of invoice document. PNG, JPG, and PDF are supported. 10MB max. If the invoice already has a document, this will add a new document to the invoice.

        uploaded_image : typing.Optional[str]
            DEPRECATED. Use document field instead.

        creator_user_id : typing.Optional[EntityUserId]
            ID of entity user who created this invoice.

        failure_type : typing.Optional[InvoiceFailureType]
            If the invoice failed to be paid, indicate the failure reason. Only applicable for invoices with custom payment methods.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        InvoiceResponse

        Examples
        --------
        import datetime

        from mercoa import InvoiceLineItemRequest, PaymentDestinationOptions_Check
        from mercoa.client import Mercoa

        client = Mercoa(
            token="YOUR_TOKEN",
        )
        client.invoice.create(
            status="NEW",
            amount=100.0,
            currency="USD",
            invoice_date=datetime.datetime.fromisoformat(
                "2021-01-01 00:00:00+00:00",
            ),
            due_date=datetime.datetime.fromisoformat(
                "2021-01-31 00:00:00+00:00",
            ),
            invoice_number="INV-123",
            note_to_self="For the month of January",
            payer_id="ent_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            payment_source_id="pm_4794d597-70dc-4fec-b6ec-c5988e759769",
            vendor_id="ent_21661ac1-a2a8-4465-a6c0-64474ba8181d",
            payment_destination_id="pm_5fde2f4a-facc-48ef-8f0d-6b7d087c7b18",
            payment_destination_options=PaymentDestinationOptions_Check(
                delivery="MAIL",
            ),
            line_items=[
                InvoiceLineItemRequest(
                    amount=100.0,
                    currency="USD",
                    description="Product A",
                    name="Product A",
                    quantity=1,
                    unit_price=100.0,
                    service_start_date=datetime.datetime.fromisoformat(
                        "2021-01-01 00:00:00+00:00",
                    ),
                    service_end_date=datetime.datetime.fromisoformat(
                        "2021-01-31 00:00:00+00:00",
                    ),
                    metadata={"key1": "value1", "key2": "value2"},
                    gl_account_id="600394",
                )
            ],
            creator_entity_id="ent_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            creator_user_id="user_e24fc81c-c5ee-47e8-af42-4fe29d895506",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "invoice",
            method="POST",
            json={
                "creatorEntityId": creator_entity_id,
                "status": status,
                "amount": amount,
                "currency": currency,
                "invoiceDate": invoice_date,
                "deductionDate": deduction_date,
                "settlementDate": settlement_date,
                "dueDate": due_date,
                "invoiceNumber": invoice_number,
                "noteToSelf": note_to_self,
                "serviceStartDate": service_start_date,
                "serviceEndDate": service_end_date,
                "payerId": payer_id,
                "paymentSourceId": payment_source_id,
                "vendorId": vendor_id,
                "paymentDestinationId": payment_destination_id,
                "paymentDestinationOptions": payment_destination_options,
                "approvers": approvers,
                "lineItems": line_items,
                "metadata": metadata,
                "foreignId": foreign_id,
                "document": document,
                "uploadedImage": uploaded_image,
                "creatorUserId": creator_user_id,
                "failureType": failure_type,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        if 200 <= _response.status_code < 300:
            return pydantic_v1.parse_obj_as(InvoiceResponse, _response_json)  # type: ignore
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "NotFound":
                raise NotFound(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Conflict":
                raise Conflict(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def get(
        self,
        invoice_id: InvoiceId,
        *,
        include_fees: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> InvoiceResponse:
        """
        Parameters
        ----------
        invoice_id : InvoiceId

        include_fees : typing.Optional[bool]
            DEPRECATED. Fees are now included by default in the response.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        InvoiceResponse

        Examples
        --------
        from mercoa.client import Mercoa

        client = Mercoa(
            token="YOUR_TOKEN",
        )
        client.invoice.get(
            invoice_id="inv_8545a84e-a45f-41bf-bdf1-33b42a55812c",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"invoice/{jsonable_encoder(invoice_id)}",
            method="GET",
            params={"includeFees": include_fees},
            request_options=request_options,
        )
        try:
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        if 200 <= _response.status_code < 300:
            return pydantic_v1.parse_obj_as(InvoiceResponse, _response_json)  # type: ignore
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "NotFound":
                raise NotFound(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Conflict":
                raise Conflict(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def update(
        self,
        invoice_id: InvoiceId,
        *,
        creator_entity_id: typing.Optional[EntityId] = OMIT,
        status: typing.Optional[InvoiceStatus] = OMIT,
        amount: typing.Optional[float] = OMIT,
        currency: typing.Optional[CurrencyCode] = OMIT,
        invoice_date: typing.Optional[dt.datetime] = OMIT,
        deduction_date: typing.Optional[dt.datetime] = OMIT,
        settlement_date: typing.Optional[dt.datetime] = OMIT,
        due_date: typing.Optional[dt.datetime] = OMIT,
        invoice_number: typing.Optional[str] = OMIT,
        note_to_self: typing.Optional[str] = OMIT,
        service_start_date: typing.Optional[dt.datetime] = OMIT,
        service_end_date: typing.Optional[dt.datetime] = OMIT,
        payer_id: typing.Optional[EntityId] = OMIT,
        payment_source_id: typing.Optional[PaymentMethodId] = OMIT,
        vendor_id: typing.Optional[EntityId] = OMIT,
        payment_destination_id: typing.Optional[PaymentMethodId] = OMIT,
        payment_destination_options: typing.Optional[PaymentDestinationOptions] = OMIT,
        approvers: typing.Optional[typing.Sequence[ApprovalSlotAssignment]] = OMIT,
        line_items: typing.Optional[typing.Sequence[InvoiceLineItemRequest]] = OMIT,
        metadata: typing.Optional[typing.Dict[str, str]] = OMIT,
        foreign_id: typing.Optional[str] = OMIT,
        document: typing.Optional[str] = OMIT,
        uploaded_image: typing.Optional[str] = OMIT,
        creator_user_id: typing.Optional[EntityUserId] = OMIT,
        failure_type: typing.Optional[InvoiceFailureType] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> InvoiceResponse:
        """
        Parameters
        ----------
        invoice_id : InvoiceId

        creator_entity_id : typing.Optional[EntityId]
            ID of entity who created this invoice. If creating a payable invoice (AP), this must be the same as the payerId. If creating a receivable invoice (AR), this must be the same as the vendorId.

        status : typing.Optional[InvoiceStatus]

        amount : typing.Optional[float]
            Total amount of invoice in major units. If the entered amount has more decimal places than the currency supports, trailing decimals will be truncated.

        currency : typing.Optional[CurrencyCode]
            Currency code for the amount. Defaults to USD.

        invoice_date : typing.Optional[dt.datetime]
            Date the invoice was issued.

        deduction_date : typing.Optional[dt.datetime]
            Date when funds are scheduled to be deducted from payer's account.

        settlement_date : typing.Optional[dt.datetime]
            Date of funds settlement.

        due_date : typing.Optional[dt.datetime]
            Due date of invoice.

        invoice_number : typing.Optional[str]

        note_to_self : typing.Optional[str]
            Note to self or memo on invoice.

        service_start_date : typing.Optional[dt.datetime]

        service_end_date : typing.Optional[dt.datetime]

        payer_id : typing.Optional[EntityId]

        payment_source_id : typing.Optional[PaymentMethodId]
            ID of payment source for this invoice. If not provided, will attempt to use the default payment source for the payer when creating an invoice if a default payment source exists for the payer.

        vendor_id : typing.Optional[EntityId]

        payment_destination_id : typing.Optional[PaymentMethodId]
            ID of payment destination for this invoice. If not provided, will attempt to use the default payment destination for the vendor when creating an invoice if a default payment destination exists for the vendor.

        payment_destination_options : typing.Optional[PaymentDestinationOptions]
            Options for the payment destination. Depending on the payment destination, this may include things such as check delivery method.

        approvers : typing.Optional[typing.Sequence[ApprovalSlotAssignment]]
            Set approvers for this invoice.

        line_items : typing.Optional[typing.Sequence[InvoiceLineItemRequest]]

        metadata : typing.Optional[typing.Dict[str, str]]
            Metadata associated with this invoice. You can specify up to 10 keys, with key names up to 40 characters long and values up to 200 characters long.

        foreign_id : typing.Optional[str]
            The ID used to identify this invoice in your system. This ID must be unique within each creatorEntity in your system, e.g. two invoices with the same creatorEntity may not have the same foreign ID.

        document : typing.Optional[str]
            Base64 encoded image or PDF of invoice document. PNG, JPG, and PDF are supported. 10MB max. If the invoice already has a document, this will add a new document to the invoice.

        uploaded_image : typing.Optional[str]
            DEPRECATED. Use document field instead.

        creator_user_id : typing.Optional[EntityUserId]
            ID of entity user who created this invoice.

        failure_type : typing.Optional[InvoiceFailureType]
            If the invoice failed to be paid, indicate the failure reason. Only applicable for invoices with custom payment methods.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        InvoiceResponse

        Examples
        --------
        import datetime

        from mercoa import InvoiceLineItemRequest, PaymentDestinationOptions_Check
        from mercoa.client import Mercoa

        client = Mercoa(
            token="YOUR_TOKEN",
        )
        client.invoice.update(
            invoice_id="inv_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            status="NEW",
            amount=100.0,
            currency="USD",
            invoice_date=datetime.datetime.fromisoformat(
                "2021-01-01 00:00:00+00:00",
            ),
            due_date=datetime.datetime.fromisoformat(
                "2021-01-31 00:00:00+00:00",
            ),
            invoice_number="INV-123",
            note_to_self="For the month of January",
            payer_id="ent_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            payment_source_id="pm_4794d597-70dc-4fec-b6ec-c5988e759769",
            vendor_id="ent_21661ac1-a2a8-4465-a6c0-64474ba8181d",
            payment_destination_id="pm_5fde2f4a-facc-48ef-8f0d-6b7d087c7b18",
            payment_destination_options=PaymentDestinationOptions_Check(
                delivery="MAIL",
            ),
            line_items=[
                InvoiceLineItemRequest(
                    amount=100.0,
                    currency="USD",
                    description="Product A",
                    name="Product A",
                    quantity=1,
                    unit_price=100.0,
                    service_start_date=datetime.datetime.fromisoformat(
                        "2021-01-01 00:00:00+00:00",
                    ),
                    service_end_date=datetime.datetime.fromisoformat(
                        "2021-01-31 00:00:00+00:00",
                    ),
                    metadata={"key1": "value1", "key2": "value2"},
                    gl_account_id="600394",
                )
            ],
            creator_entity_id="ent_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            creator_user_id="user_e24fc81c-c5ee-47e8-af42-4fe29d895506",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"invoice/{jsonable_encoder(invoice_id)}",
            method="POST",
            json={
                "creatorEntityId": creator_entity_id,
                "status": status,
                "amount": amount,
                "currency": currency,
                "invoiceDate": invoice_date,
                "deductionDate": deduction_date,
                "settlementDate": settlement_date,
                "dueDate": due_date,
                "invoiceNumber": invoice_number,
                "noteToSelf": note_to_self,
                "serviceStartDate": service_start_date,
                "serviceEndDate": service_end_date,
                "payerId": payer_id,
                "paymentSourceId": payment_source_id,
                "vendorId": vendor_id,
                "paymentDestinationId": payment_destination_id,
                "paymentDestinationOptions": payment_destination_options,
                "approvers": approvers,
                "lineItems": line_items,
                "metadata": metadata,
                "foreignId": foreign_id,
                "document": document,
                "uploadedImage": uploaded_image,
                "creatorUserId": creator_user_id,
                "failureType": failure_type,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        if 200 <= _response.status_code < 300:
            return pydantic_v1.parse_obj_as(InvoiceResponse, _response_json)  # type: ignore
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "NotFound":
                raise NotFound(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Conflict":
                raise Conflict(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def delete(self, invoice_id: InvoiceId, *, request_options: typing.Optional[RequestOptions] = None) -> None:
        """
        Only invoices in the DRAFT and NEW status can be deleted.

        Parameters
        ----------
        invoice_id : InvoiceId

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        None

        Examples
        --------
        from mercoa.client import Mercoa

        client = Mercoa(
            token="YOUR_TOKEN",
        )
        client.invoice.delete(
            invoice_id="inv_8545a84e-a45f-41bf-bdf1-33b42a55812c",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"invoice/{jsonable_encoder(invoice_id)}", method="DELETE", request_options=request_options
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "NotFound":
                raise NotFound(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Conflict":
                raise Conflict(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
        raise ApiError(status_code=_response.status_code, body=_response_json)


class AsyncInvoiceClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper
        self.approval = AsyncApprovalClient(client_wrapper=self._client_wrapper)
        self.comment = AsyncCommentClient(client_wrapper=self._client_wrapper)
        self.document = AsyncDocumentClient(client_wrapper=self._client_wrapper)
        self.payment_links = AsyncPaymentLinksClient(client_wrapper=self._client_wrapper)

    async def find(
        self,
        *,
        entity_id: typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]] = None,
        start_date: typing.Optional[dt.datetime] = None,
        end_date: typing.Optional[dt.datetime] = None,
        order_by: typing.Optional[InvoiceOrderByField] = None,
        order_direction: typing.Optional[OrderDirection] = None,
        limit: typing.Optional[int] = None,
        starting_after: typing.Optional[InvoiceId] = None,
        search: typing.Optional[str] = None,
        metadata: typing.Optional[typing.Union[InvoiceMetadataFilter, typing.Sequence[InvoiceMetadataFilter]]] = None,
        payer_id: typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]] = None,
        vendor_id: typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]] = None,
        approver_id: typing.Optional[typing.Union[EntityUserId, typing.Sequence[EntityUserId]]] = None,
        approver_action: typing.Optional[typing.Union[ApproverAction, typing.Sequence[ApproverAction]]] = None,
        invoice_id: typing.Optional[typing.Union[InvoiceId, typing.Sequence[InvoiceId]]] = None,
        status: typing.Optional[typing.Union[InvoiceStatus, typing.Sequence[InvoiceStatus]]] = None,
        include_fees: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> FindInvoiceResponse:
        """
        Search invoices for all entities in the organization

        Parameters
        ----------
        entity_id : typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]]
            Filter invoices by the ID of the entity that created the invoice.

        start_date : typing.Optional[dt.datetime]
            Start date for invoice created on date filter.

        end_date : typing.Optional[dt.datetime]
            End date for invoice created date filter.

        order_by : typing.Optional[InvoiceOrderByField]
            Field to order invoices by. Defaults to CREATED_AT.

        order_direction : typing.Optional[OrderDirection]
            Direction to order invoices by. Defaults to asc.

        limit : typing.Optional[int]
            Number of invoices to return. Limit can range between 1 and 100, and the default is 10.

        starting_after : typing.Optional[InvoiceId]
            The ID of the invoice to start after. If not provided, the first page of invoices will be returned.

        search : typing.Optional[str]
            Find invoices by vendor name, invoice number, or amount. Partial matches are supported.

        metadata : typing.Optional[typing.Union[InvoiceMetadataFilter, typing.Sequence[InvoiceMetadataFilter]]]
            Filter invoices by metadata. Each filter will be applied as an AND condition. Duplicate keys will be ignored.

        payer_id : typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]]
            Filter invoices by payer ID.

        vendor_id : typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]]
            Filter invoices by vendor ID.

        approver_id : typing.Optional[typing.Union[EntityUserId, typing.Sequence[EntityUserId]]]
            Filter invoices by assigned approver user ID.

        approver_action : typing.Optional[typing.Union[ApproverAction, typing.Sequence[ApproverAction]]]
            Filter invoices by approver action. Needs to be used with approverId. For example, if you want to find all invoices that have been approved by a specific user, you would use approverId and approverAction=APPROVE.

        invoice_id : typing.Optional[typing.Union[InvoiceId, typing.Sequence[InvoiceId]]]
            Filter invoices by invoice ID.

        status : typing.Optional[typing.Union[InvoiceStatus, typing.Sequence[InvoiceStatus]]]
            Invoice status to filter on

        include_fees : typing.Optional[bool]
            DEPRECATED. Fees are now included by default in the response.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        FindInvoiceResponse

        Examples
        --------
        from mercoa.client import AsyncMercoa

        client = AsyncMercoa(
            token="YOUR_TOKEN",
        )
        await client.invoice.find(
            entity_id="ent_8545a84e-a45f-41bf-bdf1-33b42a55812c",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "invoices",
            method="GET",
            params={
                "entityId": entity_id,
                "startDate": serialize_datetime(start_date) if start_date is not None else None,
                "endDate": serialize_datetime(end_date) if end_date is not None else None,
                "orderBy": order_by,
                "orderDirection": order_direction,
                "limit": limit,
                "startingAfter": starting_after,
                "search": search,
                "metadata": jsonable_encoder(metadata),
                "payerId": payer_id,
                "vendorId": vendor_id,
                "approverId": approver_id,
                "approverAction": approver_action,
                "invoiceId": invoice_id,
                "status": status,
                "includeFees": include_fees,
            },
            request_options=request_options,
        )
        try:
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        if 200 <= _response.status_code < 300:
            return pydantic_v1.parse_obj_as(FindInvoiceResponse, _response_json)  # type: ignore
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "NotFound":
                raise NotFound(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Conflict":
                raise Conflict(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def create(
        self,
        *,
        creator_entity_id: EntityId,
        status: typing.Optional[InvoiceStatus] = OMIT,
        amount: typing.Optional[float] = OMIT,
        currency: typing.Optional[CurrencyCode] = OMIT,
        invoice_date: typing.Optional[dt.datetime] = OMIT,
        deduction_date: typing.Optional[dt.datetime] = OMIT,
        settlement_date: typing.Optional[dt.datetime] = OMIT,
        due_date: typing.Optional[dt.datetime] = OMIT,
        invoice_number: typing.Optional[str] = OMIT,
        note_to_self: typing.Optional[str] = OMIT,
        service_start_date: typing.Optional[dt.datetime] = OMIT,
        service_end_date: typing.Optional[dt.datetime] = OMIT,
        payer_id: typing.Optional[EntityId] = OMIT,
        payment_source_id: typing.Optional[PaymentMethodId] = OMIT,
        vendor_id: typing.Optional[EntityId] = OMIT,
        payment_destination_id: typing.Optional[PaymentMethodId] = OMIT,
        payment_destination_options: typing.Optional[PaymentDestinationOptions] = OMIT,
        approvers: typing.Optional[typing.Sequence[ApprovalSlotAssignment]] = OMIT,
        line_items: typing.Optional[typing.Sequence[InvoiceLineItemRequest]] = OMIT,
        metadata: typing.Optional[typing.Dict[str, str]] = OMIT,
        foreign_id: typing.Optional[str] = OMIT,
        document: typing.Optional[str] = OMIT,
        uploaded_image: typing.Optional[str] = OMIT,
        creator_user_id: typing.Optional[EntityUserId] = OMIT,
        failure_type: typing.Optional[InvoiceFailureType] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> InvoiceResponse:
        """
        Parameters
        ----------
        creator_entity_id : EntityId
            ID of entity who created this invoice.

        status : typing.Optional[InvoiceStatus]

        amount : typing.Optional[float]
            Total amount of invoice in major units. If the entered amount has more decimal places than the currency supports, trailing decimals will be truncated.

        currency : typing.Optional[CurrencyCode]
            Currency code for the amount. Defaults to USD.

        invoice_date : typing.Optional[dt.datetime]
            Date the invoice was issued.

        deduction_date : typing.Optional[dt.datetime]
            Date when funds are scheduled to be deducted from payer's account.

        settlement_date : typing.Optional[dt.datetime]
            Date of funds settlement.

        due_date : typing.Optional[dt.datetime]
            Due date of invoice.

        invoice_number : typing.Optional[str]

        note_to_self : typing.Optional[str]
            Note to self or memo on invoice.

        service_start_date : typing.Optional[dt.datetime]

        service_end_date : typing.Optional[dt.datetime]

        payer_id : typing.Optional[EntityId]

        payment_source_id : typing.Optional[PaymentMethodId]
            ID of payment source for this invoice. If not provided, will attempt to use the default payment source for the payer when creating an invoice if a default payment source exists for the payer.

        vendor_id : typing.Optional[EntityId]

        payment_destination_id : typing.Optional[PaymentMethodId]
            ID of payment destination for this invoice. If not provided, will attempt to use the default payment destination for the vendor when creating an invoice if a default payment destination exists for the vendor.

        payment_destination_options : typing.Optional[PaymentDestinationOptions]
            Options for the payment destination. Depending on the payment destination, this may include things such as check delivery method.

        approvers : typing.Optional[typing.Sequence[ApprovalSlotAssignment]]
            Set approvers for this invoice.

        line_items : typing.Optional[typing.Sequence[InvoiceLineItemRequest]]

        metadata : typing.Optional[typing.Dict[str, str]]
            Metadata associated with this invoice. You can specify up to 10 keys, with key names up to 40 characters long and values up to 200 characters long.

        foreign_id : typing.Optional[str]
            The ID used to identify this invoice in your system. This ID must be unique within each creatorEntity in your system, e.g. two invoices with the same creatorEntity may not have the same foreign ID.

        document : typing.Optional[str]
            Base64 encoded image or PDF of invoice document. PNG, JPG, and PDF are supported. 10MB max. If the invoice already has a document, this will add a new document to the invoice.

        uploaded_image : typing.Optional[str]
            DEPRECATED. Use document field instead.

        creator_user_id : typing.Optional[EntityUserId]
            ID of entity user who created this invoice.

        failure_type : typing.Optional[InvoiceFailureType]
            If the invoice failed to be paid, indicate the failure reason. Only applicable for invoices with custom payment methods.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        InvoiceResponse

        Examples
        --------
        import datetime

        from mercoa import InvoiceLineItemRequest, PaymentDestinationOptions_Check
        from mercoa.client import AsyncMercoa

        client = AsyncMercoa(
            token="YOUR_TOKEN",
        )
        await client.invoice.create(
            status="NEW",
            amount=100.0,
            currency="USD",
            invoice_date=datetime.datetime.fromisoformat(
                "2021-01-01 00:00:00+00:00",
            ),
            due_date=datetime.datetime.fromisoformat(
                "2021-01-31 00:00:00+00:00",
            ),
            invoice_number="INV-123",
            note_to_self="For the month of January",
            payer_id="ent_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            payment_source_id="pm_4794d597-70dc-4fec-b6ec-c5988e759769",
            vendor_id="ent_21661ac1-a2a8-4465-a6c0-64474ba8181d",
            payment_destination_id="pm_5fde2f4a-facc-48ef-8f0d-6b7d087c7b18",
            payment_destination_options=PaymentDestinationOptions_Check(
                delivery="MAIL",
            ),
            line_items=[
                InvoiceLineItemRequest(
                    amount=100.0,
                    currency="USD",
                    description="Product A",
                    name="Product A",
                    quantity=1,
                    unit_price=100.0,
                    service_start_date=datetime.datetime.fromisoformat(
                        "2021-01-01 00:00:00+00:00",
                    ),
                    service_end_date=datetime.datetime.fromisoformat(
                        "2021-01-31 00:00:00+00:00",
                    ),
                    metadata={"key1": "value1", "key2": "value2"},
                    gl_account_id="600394",
                )
            ],
            creator_entity_id="ent_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            creator_user_id="user_e24fc81c-c5ee-47e8-af42-4fe29d895506",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "invoice",
            method="POST",
            json={
                "creatorEntityId": creator_entity_id,
                "status": status,
                "amount": amount,
                "currency": currency,
                "invoiceDate": invoice_date,
                "deductionDate": deduction_date,
                "settlementDate": settlement_date,
                "dueDate": due_date,
                "invoiceNumber": invoice_number,
                "noteToSelf": note_to_self,
                "serviceStartDate": service_start_date,
                "serviceEndDate": service_end_date,
                "payerId": payer_id,
                "paymentSourceId": payment_source_id,
                "vendorId": vendor_id,
                "paymentDestinationId": payment_destination_id,
                "paymentDestinationOptions": payment_destination_options,
                "approvers": approvers,
                "lineItems": line_items,
                "metadata": metadata,
                "foreignId": foreign_id,
                "document": document,
                "uploadedImage": uploaded_image,
                "creatorUserId": creator_user_id,
                "failureType": failure_type,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        if 200 <= _response.status_code < 300:
            return pydantic_v1.parse_obj_as(InvoiceResponse, _response_json)  # type: ignore
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "NotFound":
                raise NotFound(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Conflict":
                raise Conflict(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def get(
        self,
        invoice_id: InvoiceId,
        *,
        include_fees: typing.Optional[bool] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> InvoiceResponse:
        """
        Parameters
        ----------
        invoice_id : InvoiceId

        include_fees : typing.Optional[bool]
            DEPRECATED. Fees are now included by default in the response.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        InvoiceResponse

        Examples
        --------
        from mercoa.client import AsyncMercoa

        client = AsyncMercoa(
            token="YOUR_TOKEN",
        )
        await client.invoice.get(
            invoice_id="inv_8545a84e-a45f-41bf-bdf1-33b42a55812c",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"invoice/{jsonable_encoder(invoice_id)}",
            method="GET",
            params={"includeFees": include_fees},
            request_options=request_options,
        )
        try:
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        if 200 <= _response.status_code < 300:
            return pydantic_v1.parse_obj_as(InvoiceResponse, _response_json)  # type: ignore
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "NotFound":
                raise NotFound(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Conflict":
                raise Conflict(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def update(
        self,
        invoice_id: InvoiceId,
        *,
        creator_entity_id: typing.Optional[EntityId] = OMIT,
        status: typing.Optional[InvoiceStatus] = OMIT,
        amount: typing.Optional[float] = OMIT,
        currency: typing.Optional[CurrencyCode] = OMIT,
        invoice_date: typing.Optional[dt.datetime] = OMIT,
        deduction_date: typing.Optional[dt.datetime] = OMIT,
        settlement_date: typing.Optional[dt.datetime] = OMIT,
        due_date: typing.Optional[dt.datetime] = OMIT,
        invoice_number: typing.Optional[str] = OMIT,
        note_to_self: typing.Optional[str] = OMIT,
        service_start_date: typing.Optional[dt.datetime] = OMIT,
        service_end_date: typing.Optional[dt.datetime] = OMIT,
        payer_id: typing.Optional[EntityId] = OMIT,
        payment_source_id: typing.Optional[PaymentMethodId] = OMIT,
        vendor_id: typing.Optional[EntityId] = OMIT,
        payment_destination_id: typing.Optional[PaymentMethodId] = OMIT,
        payment_destination_options: typing.Optional[PaymentDestinationOptions] = OMIT,
        approvers: typing.Optional[typing.Sequence[ApprovalSlotAssignment]] = OMIT,
        line_items: typing.Optional[typing.Sequence[InvoiceLineItemRequest]] = OMIT,
        metadata: typing.Optional[typing.Dict[str, str]] = OMIT,
        foreign_id: typing.Optional[str] = OMIT,
        document: typing.Optional[str] = OMIT,
        uploaded_image: typing.Optional[str] = OMIT,
        creator_user_id: typing.Optional[EntityUserId] = OMIT,
        failure_type: typing.Optional[InvoiceFailureType] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> InvoiceResponse:
        """
        Parameters
        ----------
        invoice_id : InvoiceId

        creator_entity_id : typing.Optional[EntityId]
            ID of entity who created this invoice. If creating a payable invoice (AP), this must be the same as the payerId. If creating a receivable invoice (AR), this must be the same as the vendorId.

        status : typing.Optional[InvoiceStatus]

        amount : typing.Optional[float]
            Total amount of invoice in major units. If the entered amount has more decimal places than the currency supports, trailing decimals will be truncated.

        currency : typing.Optional[CurrencyCode]
            Currency code for the amount. Defaults to USD.

        invoice_date : typing.Optional[dt.datetime]
            Date the invoice was issued.

        deduction_date : typing.Optional[dt.datetime]
            Date when funds are scheduled to be deducted from payer's account.

        settlement_date : typing.Optional[dt.datetime]
            Date of funds settlement.

        due_date : typing.Optional[dt.datetime]
            Due date of invoice.

        invoice_number : typing.Optional[str]

        note_to_self : typing.Optional[str]
            Note to self or memo on invoice.

        service_start_date : typing.Optional[dt.datetime]

        service_end_date : typing.Optional[dt.datetime]

        payer_id : typing.Optional[EntityId]

        payment_source_id : typing.Optional[PaymentMethodId]
            ID of payment source for this invoice. If not provided, will attempt to use the default payment source for the payer when creating an invoice if a default payment source exists for the payer.

        vendor_id : typing.Optional[EntityId]

        payment_destination_id : typing.Optional[PaymentMethodId]
            ID of payment destination for this invoice. If not provided, will attempt to use the default payment destination for the vendor when creating an invoice if a default payment destination exists for the vendor.

        payment_destination_options : typing.Optional[PaymentDestinationOptions]
            Options for the payment destination. Depending on the payment destination, this may include things such as check delivery method.

        approvers : typing.Optional[typing.Sequence[ApprovalSlotAssignment]]
            Set approvers for this invoice.

        line_items : typing.Optional[typing.Sequence[InvoiceLineItemRequest]]

        metadata : typing.Optional[typing.Dict[str, str]]
            Metadata associated with this invoice. You can specify up to 10 keys, with key names up to 40 characters long and values up to 200 characters long.

        foreign_id : typing.Optional[str]
            The ID used to identify this invoice in your system. This ID must be unique within each creatorEntity in your system, e.g. two invoices with the same creatorEntity may not have the same foreign ID.

        document : typing.Optional[str]
            Base64 encoded image or PDF of invoice document. PNG, JPG, and PDF are supported. 10MB max. If the invoice already has a document, this will add a new document to the invoice.

        uploaded_image : typing.Optional[str]
            DEPRECATED. Use document field instead.

        creator_user_id : typing.Optional[EntityUserId]
            ID of entity user who created this invoice.

        failure_type : typing.Optional[InvoiceFailureType]
            If the invoice failed to be paid, indicate the failure reason. Only applicable for invoices with custom payment methods.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        InvoiceResponse

        Examples
        --------
        import datetime

        from mercoa import InvoiceLineItemRequest, PaymentDestinationOptions_Check
        from mercoa.client import AsyncMercoa

        client = AsyncMercoa(
            token="YOUR_TOKEN",
        )
        await client.invoice.update(
            invoice_id="inv_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            status="NEW",
            amount=100.0,
            currency="USD",
            invoice_date=datetime.datetime.fromisoformat(
                "2021-01-01 00:00:00+00:00",
            ),
            due_date=datetime.datetime.fromisoformat(
                "2021-01-31 00:00:00+00:00",
            ),
            invoice_number="INV-123",
            note_to_self="For the month of January",
            payer_id="ent_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            payment_source_id="pm_4794d597-70dc-4fec-b6ec-c5988e759769",
            vendor_id="ent_21661ac1-a2a8-4465-a6c0-64474ba8181d",
            payment_destination_id="pm_5fde2f4a-facc-48ef-8f0d-6b7d087c7b18",
            payment_destination_options=PaymentDestinationOptions_Check(
                delivery="MAIL",
            ),
            line_items=[
                InvoiceLineItemRequest(
                    amount=100.0,
                    currency="USD",
                    description="Product A",
                    name="Product A",
                    quantity=1,
                    unit_price=100.0,
                    service_start_date=datetime.datetime.fromisoformat(
                        "2021-01-01 00:00:00+00:00",
                    ),
                    service_end_date=datetime.datetime.fromisoformat(
                        "2021-01-31 00:00:00+00:00",
                    ),
                    metadata={"key1": "value1", "key2": "value2"},
                    gl_account_id="600394",
                )
            ],
            creator_entity_id="ent_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            creator_user_id="user_e24fc81c-c5ee-47e8-af42-4fe29d895506",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"invoice/{jsonable_encoder(invoice_id)}",
            method="POST",
            json={
                "creatorEntityId": creator_entity_id,
                "status": status,
                "amount": amount,
                "currency": currency,
                "invoiceDate": invoice_date,
                "deductionDate": deduction_date,
                "settlementDate": settlement_date,
                "dueDate": due_date,
                "invoiceNumber": invoice_number,
                "noteToSelf": note_to_self,
                "serviceStartDate": service_start_date,
                "serviceEndDate": service_end_date,
                "payerId": payer_id,
                "paymentSourceId": payment_source_id,
                "vendorId": vendor_id,
                "paymentDestinationId": payment_destination_id,
                "paymentDestinationOptions": payment_destination_options,
                "approvers": approvers,
                "lineItems": line_items,
                "metadata": metadata,
                "foreignId": foreign_id,
                "document": document,
                "uploadedImage": uploaded_image,
                "creatorUserId": creator_user_id,
                "failureType": failure_type,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        if 200 <= _response.status_code < 300:
            return pydantic_v1.parse_obj_as(InvoiceResponse, _response_json)  # type: ignore
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "NotFound":
                raise NotFound(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Conflict":
                raise Conflict(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def delete(self, invoice_id: InvoiceId, *, request_options: typing.Optional[RequestOptions] = None) -> None:
        """
        Only invoices in the DRAFT and NEW status can be deleted.

        Parameters
        ----------
        invoice_id : InvoiceId

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        None

        Examples
        --------
        from mercoa.client import AsyncMercoa

        client = AsyncMercoa(
            token="YOUR_TOKEN",
        )
        await client.invoice.delete(
            invoice_id="inv_8545a84e-a45f-41bf-bdf1-33b42a55812c",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"invoice/{jsonable_encoder(invoice_id)}", method="DELETE", request_options=request_options
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "NotFound":
                raise NotFound(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Conflict":
                raise Conflict(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(pydantic_v1.parse_obj_as(str, _response_json["content"]))  # type: ignore
        raise ApiError(status_code=_response.status_code, body=_response_json)
