"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""

from __future__ import annotations
from ascend_sdk import utils
from ascend_sdk.types import (
    BaseModel,
    Nullable,
    OptionalNullable,
    UNSET,
    UNSET_SENTINEL,
)
from ascend_sdk.utils import validate_open_enum
from datetime import datetime
from enum import Enum
from pydantic import model_serializer
from pydantic.functional_validators import PlainValidator
from typing import Any, Dict, Optional
from typing_extensions import Annotated, NotRequired, TypedDict


class CashJournalAmountTypedDict(TypedDict):
    r"""The amount to transfer in USD"""

    value: NotRequired[str]
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class CashJournalAmount(BaseModel):
    r"""The amount to transfer in USD"""

    value: Optional[str] = None
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class PartyType(str, Enum, metaclass=utils.OpenEnumMeta):
    r"""Whether a cash journal is first party or third party Determined asynchronously when the transfer is processing, and will be set by the time the transfer is posted"""

    PARTY_TYPE_UNSPECIFIED = "PARTY_TYPE_UNSPECIFIED"
    FIRST_PARTY = "FIRST_PARTY"
    THIRD_PARTY = "THIRD_PARTY"


class CashJournalType(str, Enum, metaclass=utils.OpenEnumMeta):
    r"""The type of retirement contribution."""

    TYPE_UNSPECIFIED = "TYPE_UNSPECIFIED"
    REGULAR = "REGULAR"
    EMPLOYEE = "EMPLOYEE"
    EMPLOYER = "EMPLOYER"
    RECHARACTERIZATION = "RECHARACTERIZATION"
    ROLLOVER_60_DAY = "ROLLOVER_60_DAY"
    ROLLOVER_DIRECT = "ROLLOVER_DIRECT"
    TRANSFER = "TRANSFER"
    TRUSTEE_FEE = "TRUSTEE_FEE"
    CONVERSION = "CONVERSION"
    REPAYMENT = "REPAYMENT"


class CashJournalRetirementContributionTypedDict(TypedDict):
    r"""The retirement contribution details Must be provided when the destination account is a retirement account"""

    tax_year: NotRequired[int]
    r"""Tax year for which the contribution is applied. Current year is always valid; prior year is only valid before tax deadline. Must be in \"YYYY\" format."""
    type: NotRequired[CashJournalType]
    r"""The type of retirement contribution."""


class CashJournalRetirementContribution(BaseModel):
    r"""The retirement contribution details Must be provided when the destination account is a retirement account"""

    tax_year: Optional[int] = None
    r"""Tax year for which the contribution is applied. Current year is always valid; prior year is only valid before tax deadline. Must be in \"YYYY\" format."""

    type: Annotated[
        Optional[CashJournalType], PlainValidator(validate_open_enum(False))
    ] = None
    r"""The type of retirement contribution."""


class CashJournalRetirementDistributionAmountTypedDict(TypedDict):
    r"""Fixed USD amount to withhold for taxes."""

    value: NotRequired[str]
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class CashJournalRetirementDistributionAmount(BaseModel):
    r"""Fixed USD amount to withhold for taxes."""

    value: Optional[str] = None
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class CashJournalPercentageTypedDict(TypedDict):
    r"""Percentage of total disbursement amount to withhold for taxes."""

    value: NotRequired[str]
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class CashJournalPercentage(BaseModel):
    r"""Percentage of total disbursement amount to withhold for taxes."""

    value: Optional[str] = None
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class CashJournalFederalTaxWithholdingTypedDict(TypedDict):
    r"""The federal tax withholding."""

    amount: NotRequired[Nullable[CashJournalRetirementDistributionAmountTypedDict]]
    r"""Fixed USD amount to withhold for taxes."""
    percentage: NotRequired[Nullable[CashJournalPercentageTypedDict]]
    r"""Percentage of total disbursement amount to withhold for taxes."""


class CashJournalFederalTaxWithholding(BaseModel):
    r"""The federal tax withholding."""

    amount: OptionalNullable[CashJournalRetirementDistributionAmount] = UNSET
    r"""Fixed USD amount to withhold for taxes."""

    percentage: OptionalNullable[CashJournalPercentage] = UNSET
    r"""Percentage of total disbursement amount to withhold for taxes."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["amount", "percentage"]
        nullable_fields = ["amount", "percentage"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in self.model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class CashJournalRetirementDistributionStateTaxWithholdingAmountTypedDict(TypedDict):
    r"""Fixed USD amount to withhold for taxes."""

    value: NotRequired[str]
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class CashJournalRetirementDistributionStateTaxWithholdingAmount(BaseModel):
    r"""Fixed USD amount to withhold for taxes."""

    value: Optional[str] = None
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class CashJournalRetirementDistributionPercentageTypedDict(TypedDict):
    r"""Percentage of total disbursement amount to withhold for taxes."""

    value: NotRequired[str]
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class CashJournalRetirementDistributionPercentage(BaseModel):
    r"""Percentage of total disbursement amount to withhold for taxes."""

    value: Optional[str] = None
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class CashJournalStateTaxWithholdingTypedDict(TypedDict):
    r"""The state tax withholding."""

    amount: NotRequired[
        Nullable[CashJournalRetirementDistributionStateTaxWithholdingAmountTypedDict]
    ]
    r"""Fixed USD amount to withhold for taxes."""
    percentage: NotRequired[
        Nullable[CashJournalRetirementDistributionPercentageTypedDict]
    ]
    r"""Percentage of total disbursement amount to withhold for taxes."""


class CashJournalStateTaxWithholding(BaseModel):
    r"""The state tax withholding."""

    amount: OptionalNullable[
        CashJournalRetirementDistributionStateTaxWithholdingAmount
    ] = UNSET
    r"""Fixed USD amount to withhold for taxes."""

    percentage: OptionalNullable[CashJournalRetirementDistributionPercentage] = UNSET
    r"""Percentage of total disbursement amount to withhold for taxes."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["amount", "percentage"]
        nullable_fields = ["amount", "percentage"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in self.model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class CashJournalRetirementDistributionType(str, Enum, metaclass=utils.OpenEnumMeta):
    r"""The type of retirement distribution."""

    TYPE_UNSPECIFIED = "TYPE_UNSPECIFIED"
    NORMAL = "NORMAL"
    DISABILITY = "DISABILITY"
    SOSEPP = "SOSEPP"
    PREMATURE = "PREMATURE"
    DEATH = "DEATH"
    EXCESS_CONTRIBUTION_REMOVAL_BEFORE_TAX_DEADLINE = (
        "EXCESS_CONTRIBUTION_REMOVAL_BEFORE_TAX_DEADLINE"
    )
    EXCESS_CONTRIBUTION_REMOVAL_AFTER_TAX_DEADLINE = (
        "EXCESS_CONTRIBUTION_REMOVAL_AFTER_TAX_DEADLINE"
    )
    ROLLOVER_TO_QUALIFIED_PLAN = "ROLLOVER_TO_QUALIFIED_PLAN"
    ROLLOVER_TO_IRA = "ROLLOVER_TO_IRA"
    DISTRIBUTION_TRANSFER = "DISTRIBUTION_TRANSFER"
    RECHARACTERIZATION_PRIOR_YEAR = "RECHARACTERIZATION_PRIOR_YEAR"
    RECHARACTERIZATION_CURRENT_YEAR = "RECHARACTERIZATION_CURRENT_YEAR"
    DISTRIBUTION_CONVERSION = "DISTRIBUTION_CONVERSION"
    MANAGEMENT_FEE = "MANAGEMENT_FEE"
    PLAN_LOAN_401_K = "PLAN_LOAN_401K"
    PREMATURE_SIMPLE_IRA_LESS_THAN_2_YEARS = "PREMATURE_SIMPLE_IRA_LESS_THAN_2_YEARS"
    NORMAL_ROTH_IRA_GREATER_THAN_5_YEARS = "NORMAL_ROTH_IRA_GREATER_THAN_5_YEARS"


class CashJournalRetirementDistributionTypedDict(TypedDict):
    r"""The retirement distribution details Must be provided when the source account is a retirement account"""

    federal_tax_withholding: NotRequired[
        Nullable[CashJournalFederalTaxWithholdingTypedDict]
    ]
    r"""The federal tax withholding."""
    receiving_institution: NotRequired[str]
    r"""The institution receiving retirement funds when performing a transfer to an identical retirement account type at a different financial institution. This is required for check and wire withdrawals because we can't always identify the institution using the transfer instructions. For cash journals this value will default to \"Apex Clearing\", regardless of what is passed in here"""
    state_tax_withholding: NotRequired[
        Nullable[CashJournalStateTaxWithholdingTypedDict]
    ]
    r"""The state tax withholding."""
    state_withholding_waiver: NotRequired[bool]
    r"""Whether or not this distribution has a state withholding waiver."""
    tax_year: NotRequired[int]
    r"""Tax year for which the distribution is applied."""
    type: NotRequired[CashJournalRetirementDistributionType]
    r"""The type of retirement distribution."""


class CashJournalRetirementDistribution(BaseModel):
    r"""The retirement distribution details Must be provided when the source account is a retirement account"""

    federal_tax_withholding: OptionalNullable[CashJournalFederalTaxWithholding] = UNSET
    r"""The federal tax withholding."""

    receiving_institution: Optional[str] = None
    r"""The institution receiving retirement funds when performing a transfer to an identical retirement account type at a different financial institution. This is required for check and wire withdrawals because we can't always identify the institution using the transfer instructions. For cash journals this value will default to \"Apex Clearing\", regardless of what is passed in here"""

    state_tax_withholding: OptionalNullable[CashJournalStateTaxWithholding] = UNSET
    r"""The state tax withholding."""

    state_withholding_waiver: Optional[bool] = None
    r"""Whether or not this distribution has a state withholding waiver."""

    tax_year: Optional[int] = None
    r"""Tax year for which the distribution is applied."""

    type: Annotated[
        Optional[CashJournalRetirementDistributionType],
        PlainValidator(validate_open_enum(False)),
    ] = None
    r"""The type of retirement distribution."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "federal_tax_withholding",
            "receiving_institution",
            "state_tax_withholding",
            "state_withholding_waiver",
            "tax_year",
            "type",
        ]
        nullable_fields = ["federal_tax_withholding", "state_tax_withholding"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in self.model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class CashJournalStateState(str, Enum, metaclass=utils.OpenEnumMeta):
    r"""The high level state of a transfer, one of:
    - `PROCESSING` - The transfer is being processed and will be posted if successful.
    - `PENDING_REVIEW` - The transfer is pending review and will continue processing if approved.
    - `POSTED` - The transfer has been posted to the ledger and will be completed at the end of the processing window if not canceled first.
    - `COMPLETED` - The transfer has been batched and completed.
    - `REJECTED` - The transfer was rejected.
    - `CANCELED` - The transfer was canceled.
    - `RETURNED` - The transfer was returned.
    - `POSTPONED` - The transfer is postponed and will resume processing during the next processing window.
    """

    STATE_UNSPECIFIED = "STATE_UNSPECIFIED"
    PROCESSING = "PROCESSING"
    PENDING_REVIEW = "PENDING_REVIEW"
    POSTED = "POSTED"
    COMPLETED = "COMPLETED"
    REJECTED = "REJECTED"
    CANCELED = "CANCELED"
    RETURNED = "RETURNED"
    POSTPONED = "POSTPONED"


class CashJournalStateTypedDict(TypedDict):
    r"""The current state of the cash journal"""

    actor: NotRequired[str]
    r"""The user or service that triggered the state update."""
    message: NotRequired[str]
    r"""Additional description of the transfer state."""
    metadata: NotRequired[Nullable[Dict[str, Any]]]
    r"""Additional metadata relating to the transfer state. Included data depends on the state, e.g.:
    - Rejection reasons are included when the `state` is `REJECTED`
    - Reason and comment are included when `state` is `CANCELED`
    """
    state: NotRequired[CashJournalStateState]
    r"""The high level state of a transfer, one of:
    - `PROCESSING` - The transfer is being processed and will be posted if successful.
    - `PENDING_REVIEW` - The transfer is pending review and will continue processing if approved.
    - `POSTED` - The transfer has been posted to the ledger and will be completed at the end of the processing window if not canceled first.
    - `COMPLETED` - The transfer has been batched and completed.
    - `REJECTED` - The transfer was rejected.
    - `CANCELED` - The transfer was canceled.
    - `RETURNED` - The transfer was returned.
    - `POSTPONED` - The transfer is postponed and will resume processing during the next processing window.
    """
    update_time: NotRequired[Nullable[datetime]]
    r"""The time of the state update."""


class CashJournalState(BaseModel):
    r"""The current state of the cash journal"""

    actor: Optional[str] = None
    r"""The user or service that triggered the state update."""

    message: Optional[str] = None
    r"""Additional description of the transfer state."""

    metadata: OptionalNullable[Dict[str, Any]] = UNSET
    r"""Additional metadata relating to the transfer state. Included data depends on the state, e.g.:
    - Rejection reasons are included when the `state` is `REJECTED`
    - Reason and comment are included when `state` is `CANCELED`
    """

    state: Annotated[
        Optional[CashJournalStateState], PlainValidator(validate_open_enum(False))
    ] = None
    r"""The high level state of a transfer, one of:
    - `PROCESSING` - The transfer is being processed and will be posted if successful.
    - `PENDING_REVIEW` - The transfer is pending review and will continue processing if approved.
    - `POSTED` - The transfer has been posted to the ledger and will be completed at the end of the processing window if not canceled first.
    - `COMPLETED` - The transfer has been batched and completed.
    - `REJECTED` - The transfer was rejected.
    - `CANCELED` - The transfer was canceled.
    - `RETURNED` - The transfer was returned.
    - `POSTPONED` - The transfer is postponed and will resume processing during the next processing window.
    """

    update_time: OptionalNullable[datetime] = UNSET
    r"""The time of the state update."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["actor", "message", "metadata", "state", "update_time"]
        nullable_fields = ["metadata", "update_time"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in self.model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class CashJournalTypedDict(TypedDict):
    r"""A cash journal transfer. Funds are moved from a source account to a destination account"""

    amount: NotRequired[Nullable[CashJournalAmountTypedDict]]
    r"""The amount to transfer in USD"""
    client_transfer_id: NotRequired[str]
    r"""The external identifier supplied by the API caller Each request must have a unique pairing of `client_transfer_id` and `source_account`"""
    destination_account: NotRequired[str]
    r"""The account that funds will be moved to"""
    name: NotRequired[str]
    r"""The resource name of the cash journal"""
    party_type: NotRequired[PartyType]
    r"""Whether a cash journal is first party or third party Determined asynchronously when the transfer is processing, and will be set by the time the transfer is posted"""
    retirement_contribution: NotRequired[
        Nullable[CashJournalRetirementContributionTypedDict]
    ]
    r"""The retirement contribution details Must be provided when the destination account is a retirement account"""
    retirement_distribution: NotRequired[
        Nullable[CashJournalRetirementDistributionTypedDict]
    ]
    r"""The retirement distribution details Must be provided when the source account is a retirement account"""
    source_account: NotRequired[str]
    r"""The account that funds will be moved from"""
    state: NotRequired[Nullable[CashJournalStateTypedDict]]
    r"""The current state of the cash journal"""


class CashJournal(BaseModel):
    r"""A cash journal transfer. Funds are moved from a source account to a destination account"""

    amount: OptionalNullable[CashJournalAmount] = UNSET
    r"""The amount to transfer in USD"""

    client_transfer_id: Optional[str] = None
    r"""The external identifier supplied by the API caller Each request must have a unique pairing of `client_transfer_id` and `source_account`"""

    destination_account: Optional[str] = None
    r"""The account that funds will be moved to"""

    name: Optional[str] = None
    r"""The resource name of the cash journal"""

    party_type: Annotated[
        Optional[PartyType], PlainValidator(validate_open_enum(False))
    ] = None
    r"""Whether a cash journal is first party or third party Determined asynchronously when the transfer is processing, and will be set by the time the transfer is posted"""

    retirement_contribution: OptionalNullable[CashJournalRetirementContribution] = UNSET
    r"""The retirement contribution details Must be provided when the destination account is a retirement account"""

    retirement_distribution: OptionalNullable[CashJournalRetirementDistribution] = UNSET
    r"""The retirement distribution details Must be provided when the source account is a retirement account"""

    source_account: Optional[str] = None
    r"""The account that funds will be moved from"""

    state: OptionalNullable[CashJournalState] = UNSET
    r"""The current state of the cash journal"""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "amount",
            "client_transfer_id",
            "destination_account",
            "name",
            "party_type",
            "retirement_contribution",
            "retirement_distribution",
            "source_account",
            "state",
        ]
        nullable_fields = [
            "amount",
            "retirement_contribution",
            "retirement_distribution",
            "state",
        ]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in self.model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m
