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

from ...core.client_wrapper import SyncClientWrapper
from ...entity_group_types.types.entity_group_id import EntityGroupId
import typing
import datetime as dt
from ...invoice_types.types.invoice_date_filter import InvoiceDateFilter
from ...invoice_types.types.invoice_order_by_field import InvoiceOrderByField
from ...commons.types.order_direction import OrderDirection
from ...invoice_types.types.invoice_id import InvoiceId
from ...invoice_types.types.metadata_filter import MetadataFilter
from ...entity_types.types.entity_id import EntityId
from ...entity_types.types.entity_user_id import EntityUserId
from ...invoice_types.types.approver_action import ApproverAction
from ...invoice_types.types.invoice_status import InvoiceStatus
from ...invoice_types.types.payment_type import PaymentType
from ...core.request_options import RequestOptions
from ...invoice_types.types.find_invoice_response import FindInvoiceResponse
from ...core.jsonable_encoder import jsonable_encoder
from ...core.datetime_utils import serialize_datetime
from json.decoder import JSONDecodeError
from ...core.api_error import ApiError
from ...core.pydantic_utilities import parse_obj_as
from ...commons.errors.bad_request import BadRequest
from ...commons.errors.unauthorized import Unauthorized
from ...commons.errors.forbidden import Forbidden
from ...commons.errors.not_found import NotFound
from ...commons.errors.conflict import Conflict
from ...commons.errors.internal_server_error import InternalServerError
from ...commons.errors.unimplemented import Unimplemented
from ...invoice_types.types.invoice_metrics_per_date_group_by import InvoiceMetricsPerDateGroupBy
from ...invoice_types.types.invoice_metrics_per_date_frequency import InvoiceMetricsPerDateFrequency
from ...invoice_types.types.invoice_metrics_group_by import InvoiceMetricsGroupBy
from ...payment_method_types.types.currency_code import CurrencyCode
from ...invoice_types.types.invoice_metrics_response import InvoiceMetricsResponse
from ...core.client_wrapper import AsyncClientWrapper


class InvoiceClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._client_wrapper = client_wrapper

    def find(
        self,
        entity_group_id: EntityGroupId,
        *,
        exclude_payables: typing.Optional[bool] = None,
        exclude_receivables: typing.Optional[bool] = None,
        start_date: typing.Optional[dt.datetime] = None,
        end_date: typing.Optional[dt.datetime] = None,
        date_type: typing.Optional[InvoiceDateFilter] = None,
        order_by: typing.Optional[InvoiceOrderByField] = None,
        order_direction: typing.Optional[OrderDirection] = None,
        limit: typing.Optional[int] = None,
        starting_after: typing.Optional[InvoiceId] = None,
        metadata: typing.Optional[typing.Union[MetadataFilter, typing.Sequence[MetadataFilter]]] = None,
        search: typing.Optional[str] = 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,
        payment_type: typing.Optional[typing.Sequence[PaymentType]] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> FindInvoiceResponse:
        """
        Get invoices for an entity group with the given filters.

        Parameters
        ----------
        entity_group_id : EntityGroupId
            Entity Group ID or Entity Group ForeignID

        exclude_payables : typing.Optional[bool]
            Return only invoices that are receivable by the entity.

        exclude_receivables : typing.Optional[bool]
            Return only invoices that are payable by the entity.

        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.

        date_type : typing.Optional[InvoiceDateFilter]
            Type of date to filter by if startDate and endDate filters are provided. Defaults to CREATED_AT.

        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.

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

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

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

        vendor_id : typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]]
            Filter invoices by vendor ID or vendor foreign 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.

        payment_type : typing.Optional[typing.Sequence[PaymentType]]
            Filter invoices by recurring status

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

        Returns
        -------
        FindInvoiceResponse

        Examples
        --------
        from mercoa import Mercoa

        client = Mercoa(
            token="YOUR_TOKEN",
        )
        client.entity_group.invoice.find(
            entity_group_id="entg_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            exclude_receivables=True,
            order_by="CREATED_AT",
            order_direction="ASC",
            limit=10,
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"entityGroup/{jsonable_encoder(entity_group_id)}/invoices",
            method="GET",
            params={
                "excludePayables": exclude_payables,
                "excludeReceivables": exclude_receivables,
                "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,
                "dateType": date_type,
                "orderBy": order_by,
                "orderDirection": order_direction,
                "limit": limit,
                "startingAfter": starting_after,
                "metadata": jsonable_encoder(metadata),
                "search": search,
                "payerId": payer_id,
                "vendorId": vendor_id,
                "approverId": approver_id,
                "approverAction": approver_action,
                "invoiceId": invoice_id,
                "status": status,
                "paymentType": jsonable_encoder(payment_type),
            },
            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 typing.cast(
                FindInvoiceResponse,
                parse_obj_as(
                    type_=FindInvoiceResponse,  # type: ignore
                    object_=_response_json,
                ),
            )
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "NotFound":
                raise NotFound(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Conflict":
                raise Conflict(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def metrics(
        self,
        entity_group_id: EntityGroupId,
        *,
        search: typing.Optional[str] = None,
        exclude_payables: typing.Optional[bool] = None,
        exclude_receivables: typing.Optional[bool] = None,
        return_by_date: typing.Optional[InvoiceMetricsPerDateGroupBy] = None,
        return_by_date_frequency: typing.Optional[InvoiceMetricsPerDateFrequency] = None,
        group_by: typing.Optional[typing.Union[InvoiceMetricsGroupBy, typing.Sequence[InvoiceMetricsGroupBy]]] = 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,
        invoice_id: typing.Optional[typing.Union[InvoiceId, typing.Sequence[InvoiceId]]] = None,
        status: typing.Optional[typing.Union[InvoiceStatus, typing.Sequence[InvoiceStatus]]] = None,
        start_date: typing.Optional[dt.datetime] = None,
        end_date: typing.Optional[dt.datetime] = None,
        date_type: typing.Optional[InvoiceDateFilter] = None,
        currency: typing.Optional[typing.Union[CurrencyCode, typing.Sequence[CurrencyCode]]] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.List[InvoiceMetricsResponse]:
        """
        Get invoice metrics for an entity group with the given filters. Invoices will be grouped by currency. If none of excludePayables, excludeReceivables, payerId, vendorId, or invoiceId status filters are provided, excludeReceivables will be set to true.

        Parameters
        ----------
        entity_group_id : EntityGroupId
            Entity Group ID or Entity Group ForeignID

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

        exclude_payables : typing.Optional[bool]
            Only return invoices that are not payable by the entity. This will return only invoices that are receivable by the entity.

        exclude_receivables : typing.Optional[bool]
            Only return invoices that are not receivable by the entity. This will return only invoices that are payable by the entity.

        return_by_date : typing.Optional[InvoiceMetricsPerDateGroupBy]
            Return invoice metrics grouped by date.

        return_by_date_frequency : typing.Optional[InvoiceMetricsPerDateFrequency]
            Return invoice metrics grouped by date. Defaults to daily.

        group_by : typing.Optional[typing.Union[InvoiceMetricsGroupBy, typing.Sequence[InvoiceMetricsGroupBy]]]
            Return invoice metrics grouped by.

        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.

        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

        start_date : typing.Optional[dt.datetime]
            Start date filter. Defaults to CREATED_AT unless specified the dateType is specified

        end_date : typing.Optional[dt.datetime]
            End date filter. Defaults to CREATED_AT unless specified the dateType is specified

        date_type : typing.Optional[InvoiceDateFilter]
            Type of date to filter by if startDate and endDate filters are provided. Defaults to CREATED_AT.

        currency : typing.Optional[typing.Union[CurrencyCode, typing.Sequence[CurrencyCode]]]
            Currency to filter on

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

        Returns
        -------
        typing.List[InvoiceMetricsResponse]

        Examples
        --------
        import datetime

        from mercoa import Mercoa

        client = Mercoa(
            token="YOUR_TOKEN",
        )
        client.entity_group.invoice.metrics(
            entity_group_id="entg_8545a84e-a45f-41bf-bdf1-33b42a55812c",
            return_by_date="CREATION_DATE",
            exclude_receivables=True,
            start_date=datetime.datetime.fromisoformat(
                "2021-01-01 00:00:00+00:00",
            ),
            end_date=datetime.datetime.fromisoformat(
                "2021-01-31 23:59:59.999000+00:00",
            ),
            currency="USD",
            status="NEW",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"entityGroup/{jsonable_encoder(entity_group_id)}/invoice-metrics",
            method="GET",
            params={
                "search": search,
                "excludePayables": exclude_payables,
                "excludeReceivables": exclude_receivables,
                "returnByDate": return_by_date,
                "returnByDateFrequency": return_by_date_frequency,
                "groupBy": group_by,
                "payerId": payer_id,
                "vendorId": vendor_id,
                "approverId": approver_id,
                "invoiceId": invoice_id,
                "status": status,
                "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,
                "dateType": date_type,
                "currency": currency,
            },
            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 typing.cast(
                typing.List[InvoiceMetricsResponse],
                parse_obj_as(
                    type_=typing.List[InvoiceMetricsResponse],  # type: ignore
                    object_=_response_json,
                ),
            )
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "NotFound":
                raise NotFound(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Conflict":
                raise Conflict(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
        raise ApiError(status_code=_response.status_code, body=_response_json)


class AsyncInvoiceClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    async def find(
        self,
        entity_group_id: EntityGroupId,
        *,
        exclude_payables: typing.Optional[bool] = None,
        exclude_receivables: typing.Optional[bool] = None,
        start_date: typing.Optional[dt.datetime] = None,
        end_date: typing.Optional[dt.datetime] = None,
        date_type: typing.Optional[InvoiceDateFilter] = None,
        order_by: typing.Optional[InvoiceOrderByField] = None,
        order_direction: typing.Optional[OrderDirection] = None,
        limit: typing.Optional[int] = None,
        starting_after: typing.Optional[InvoiceId] = None,
        metadata: typing.Optional[typing.Union[MetadataFilter, typing.Sequence[MetadataFilter]]] = None,
        search: typing.Optional[str] = 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,
        payment_type: typing.Optional[typing.Sequence[PaymentType]] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> FindInvoiceResponse:
        """
        Get invoices for an entity group with the given filters.

        Parameters
        ----------
        entity_group_id : EntityGroupId
            Entity Group ID or Entity Group ForeignID

        exclude_payables : typing.Optional[bool]
            Return only invoices that are receivable by the entity.

        exclude_receivables : typing.Optional[bool]
            Return only invoices that are payable by the entity.

        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.

        date_type : typing.Optional[InvoiceDateFilter]
            Type of date to filter by if startDate and endDate filters are provided. Defaults to CREATED_AT.

        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.

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

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

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

        vendor_id : typing.Optional[typing.Union[EntityId, typing.Sequence[EntityId]]]
            Filter invoices by vendor ID or vendor foreign 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.

        payment_type : typing.Optional[typing.Sequence[PaymentType]]
            Filter invoices by recurring status

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

        Returns
        -------
        FindInvoiceResponse

        Examples
        --------
        import asyncio

        from mercoa import AsyncMercoa

        client = AsyncMercoa(
            token="YOUR_TOKEN",
        )


        async def main() -> None:
            await client.entity_group.invoice.find(
                entity_group_id="entg_8545a84e-a45f-41bf-bdf1-33b42a55812c",
                exclude_receivables=True,
                order_by="CREATED_AT",
                order_direction="ASC",
                limit=10,
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"entityGroup/{jsonable_encoder(entity_group_id)}/invoices",
            method="GET",
            params={
                "excludePayables": exclude_payables,
                "excludeReceivables": exclude_receivables,
                "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,
                "dateType": date_type,
                "orderBy": order_by,
                "orderDirection": order_direction,
                "limit": limit,
                "startingAfter": starting_after,
                "metadata": jsonable_encoder(metadata),
                "search": search,
                "payerId": payer_id,
                "vendorId": vendor_id,
                "approverId": approver_id,
                "approverAction": approver_action,
                "invoiceId": invoice_id,
                "status": status,
                "paymentType": jsonable_encoder(payment_type),
            },
            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 typing.cast(
                FindInvoiceResponse,
                parse_obj_as(
                    type_=FindInvoiceResponse,  # type: ignore
                    object_=_response_json,
                ),
            )
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "NotFound":
                raise NotFound(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Conflict":
                raise Conflict(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def metrics(
        self,
        entity_group_id: EntityGroupId,
        *,
        search: typing.Optional[str] = None,
        exclude_payables: typing.Optional[bool] = None,
        exclude_receivables: typing.Optional[bool] = None,
        return_by_date: typing.Optional[InvoiceMetricsPerDateGroupBy] = None,
        return_by_date_frequency: typing.Optional[InvoiceMetricsPerDateFrequency] = None,
        group_by: typing.Optional[typing.Union[InvoiceMetricsGroupBy, typing.Sequence[InvoiceMetricsGroupBy]]] = 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,
        invoice_id: typing.Optional[typing.Union[InvoiceId, typing.Sequence[InvoiceId]]] = None,
        status: typing.Optional[typing.Union[InvoiceStatus, typing.Sequence[InvoiceStatus]]] = None,
        start_date: typing.Optional[dt.datetime] = None,
        end_date: typing.Optional[dt.datetime] = None,
        date_type: typing.Optional[InvoiceDateFilter] = None,
        currency: typing.Optional[typing.Union[CurrencyCode, typing.Sequence[CurrencyCode]]] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.List[InvoiceMetricsResponse]:
        """
        Get invoice metrics for an entity group with the given filters. Invoices will be grouped by currency. If none of excludePayables, excludeReceivables, payerId, vendorId, or invoiceId status filters are provided, excludeReceivables will be set to true.

        Parameters
        ----------
        entity_group_id : EntityGroupId
            Entity Group ID or Entity Group ForeignID

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

        exclude_payables : typing.Optional[bool]
            Only return invoices that are not payable by the entity. This will return only invoices that are receivable by the entity.

        exclude_receivables : typing.Optional[bool]
            Only return invoices that are not receivable by the entity. This will return only invoices that are payable by the entity.

        return_by_date : typing.Optional[InvoiceMetricsPerDateGroupBy]
            Return invoice metrics grouped by date.

        return_by_date_frequency : typing.Optional[InvoiceMetricsPerDateFrequency]
            Return invoice metrics grouped by date. Defaults to daily.

        group_by : typing.Optional[typing.Union[InvoiceMetricsGroupBy, typing.Sequence[InvoiceMetricsGroupBy]]]
            Return invoice metrics grouped by.

        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.

        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

        start_date : typing.Optional[dt.datetime]
            Start date filter. Defaults to CREATED_AT unless specified the dateType is specified

        end_date : typing.Optional[dt.datetime]
            End date filter. Defaults to CREATED_AT unless specified the dateType is specified

        date_type : typing.Optional[InvoiceDateFilter]
            Type of date to filter by if startDate and endDate filters are provided. Defaults to CREATED_AT.

        currency : typing.Optional[typing.Union[CurrencyCode, typing.Sequence[CurrencyCode]]]
            Currency to filter on

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

        Returns
        -------
        typing.List[InvoiceMetricsResponse]

        Examples
        --------
        import asyncio
        import datetime

        from mercoa import AsyncMercoa

        client = AsyncMercoa(
            token="YOUR_TOKEN",
        )


        async def main() -> None:
            await client.entity_group.invoice.metrics(
                entity_group_id="entg_8545a84e-a45f-41bf-bdf1-33b42a55812c",
                return_by_date="CREATION_DATE",
                exclude_receivables=True,
                start_date=datetime.datetime.fromisoformat(
                    "2021-01-01 00:00:00+00:00",
                ),
                end_date=datetime.datetime.fromisoformat(
                    "2021-01-31 23:59:59.999000+00:00",
                ),
                currency="USD",
                status="NEW",
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"entityGroup/{jsonable_encoder(entity_group_id)}/invoice-metrics",
            method="GET",
            params={
                "search": search,
                "excludePayables": exclude_payables,
                "excludeReceivables": exclude_receivables,
                "returnByDate": return_by_date,
                "returnByDateFrequency": return_by_date_frequency,
                "groupBy": group_by,
                "payerId": payer_id,
                "vendorId": vendor_id,
                "approverId": approver_id,
                "invoiceId": invoice_id,
                "status": status,
                "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,
                "dateType": date_type,
                "currency": currency,
            },
            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 typing.cast(
                typing.List[InvoiceMetricsResponse],
                parse_obj_as(
                    type_=typing.List[InvoiceMetricsResponse],  # type: ignore
                    object_=_response_json,
                ),
            )
        if "errorName" in _response_json:
            if _response_json["errorName"] == "BadRequest":
                raise BadRequest(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Unauthorized":
                raise Unauthorized(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Forbidden":
                raise Forbidden(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "NotFound":
                raise NotFound(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Conflict":
                raise Conflict(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "InternalServerError":
                raise InternalServerError(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
            if _response_json["errorName"] == "Unimplemented":
                raise Unimplemented(
                    typing.cast(
                        str,
                        parse_obj_as(
                            type_=str,  # type: ignore
                            object_=_response_json["content"],
                        ),
                    )
                )
        raise ApiError(status_code=_response.status_code, body=_response_json)
