# coding: utf-8

"""
    Acre Platform User API

    # CORS CORS is implemented by all Acre API endpoints and only certain hostnames are allowed for each deployment of the API. If your use case requires calling the Acre User API from a browser, please contact Acre to arrange the appropriate configuration. # API Authentication The primary method of authentication for users of the Acre API is with the use of JSON Web Tokens (JWTs), which are suplemented by an application specific API Key. The API key must be provided through the `X-API-KEY` HTTP header and will be provided by your Acre Customer Success Manager. The JWT must be provided through a cookie with key of `authorization`. The JWT must be generated and signed as per the following section. # Generating JWTs JWT must either be generated by a third-party such as Azure ADB2C that has been configured for the appropriate service and/or user account, or alternatively can be generated locally and signed by an private key for which the public key has been made available to Acre via a JWKS URL. In addition to being signed by your private key, the JWT will also need to contain at a minimum:  * An issuer `iss`, which must be a valid URI previously agreed with Acre.  * A subject `sub`, which must be a unique identifier for the user and/or service account previously agreed with Acre.  * An expiry time `exp` which must not be more than four hours in the future.  * A not before time `nbf` which must be in the past, but not greater than six hours in the past.  The JWT should be signed using RS256.

    The version of the OpenAPI document: 1.0
    Generated by OpenAPI Generator (https://openapi-generator.tech)

    Do not edit the class manually.
"""  # noqa: E501


from __future__ import annotations
import pprint
import re  # noqa: F401
import json

from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List, Optional
from acre_api.models.case_details_cancellation_reason import CaseDetailsCancellationReason
from acre_api.models.case_details_client_id_and_version import CaseDetailsClientIDAndVersion
from acre_api.models.case_details_conveyancer import CaseDetailsConveyancer
from acre_api.models.case_details_conveyancing_status_type import CaseDetailsConveyancingStatusType
from acre_api.models.case_details_conveyancing_type import CaseDetailsConveyancingType
from acre_api.models.case_details_conveyancing_update import CaseDetailsConveyancingUpdate
from acre_api.models.case_details_eidv_result_override import CaseDetailsEidvResultOverride
from acre_api.models.case_details_enbs_event import CaseDetailsENBSEvent
from acre_api.models.case_details_lead_metadata import CaseDetailsLeadMetadata
from acre_api.models.case_details_mortgage_reason import CaseDetailsMortgageReason
from acre_api.models.cdm_additional_loan import CdmAdditionalLoan
from acre_api.models.cdm_addn_metadata import CdmAddnMetadata
from acre_api.models.cdm_case_calculated_values import CdmCaseCalculatedValues
from acre_api.models.cdm_case_details_flag import CdmCaseDetailsFlag
from acre_api.models.cdm_case_details_note import CdmCaseDetailsNote
from acre_api.models.cdm_case_details_status import CdmCaseDetailsStatus
from acre_api.models.cdm_case_protection import CdmCaseProtection
from acre_api.models.cdm_contact_history import CdmContactHistory
from acre_api.models.cdm_external import CdmExternal
from acre_api.models.cdm_fee import CdmFee
from acre_api.models.cdm_mortgage_class import CdmMortgageClass
from acre_api.models.cdm_mortgage_id_and_version import CdmMortgageIDAndVersion
from acre_api.models.cdm_property_id_and_version import CdmPropertyIDAndVersion
from acre_api.models.cdm_protection_id_and_version import CdmProtectionIDAndVersion
from acre_api.models.cdm_referral import CdmReferral
from acre_api.models.cdm_related_case import CdmRelatedCase
from acre_api.models.cdm_related_user import CdmRelatedUser
from acre_api.models.cdm_survey_answer import CdmSurveyAnswer
from acre_api.models.cdm_third_party_org import CdmThirdPartyOrg
from acre_api.models.cdm_transition_client_versions import CdmTransitionClientVersions
from acre_api.models.cdm_transition_mortgage_versions import CdmTransitionMortgageVersions
from acre_api.models.cdm_transition_property_versions import CdmTransitionPropertyVersions
from acre_api.models.cdm_transition_protection_versions import CdmTransitionProtectionVersions
from typing import Optional, Set
from typing_extensions import Self

class CdmCaseDetails(BaseModel):
    """
    CaseDetails is the details object for Case. Case is the umbrella for the sourcing journey that a Client has, and is intended to hold more transient information such as Client preferences.
    """ # noqa: E501
    case_id: Optional[StrictStr] = Field(default=None, description="ID of the case. Automatically populated; cannot be set.")
    client_ids: Optional[List[StrictStr]] = Field(default=None, description="Clients associated with the case.")
    owner_user_id: Optional[StrictStr] = Field(default=None, description="ID of the case owner. If the person creating the case has the appropriate regulatory permissions, they will by default become the owner.  If the case is created by an non-regulated user, they will need to specify an appropriate owner within their same organisation. Can not be set if owner_organisation_id is set.")
    owner_organisation_id: Optional[StrictStr] = Field(default=None, description="ID of the owning organisation. Can only be set if no owner_user_id is set and the case is in LEAD status.")
    preference_mortgage_reason: Optional[CaseDetailsMortgageReason] = CaseDetailsMortgageReason.INVALID_MORTGAGE_REASON
    preference_related_property_sale: Optional[StrictStr] = Field(default=None, description="Property that is being sold when related to this case.")
    preference_details: Optional[StrictStr] = Field(default=None, description="The contextual details which will inform the client's decision.")
    preference_repayment_percentage: Optional[StrictInt] = Field(default=None, description="Which percentage of the loan should be on a repayment basis, represented as 1/100000 of a percent (precision for up to 5 decimal places).")
    preference_repayment_amount: Optional[StrictStr] = Field(default=None, description="The amount of the loan that should be on a repayment basis (in pence, not GBP).")
    preference_interest_only_repayment_strategy: Optional[StrictStr] = Field(default=None, description="The client's strategy to pay the capital at the end of the interest only term.")
    preference_upper_limit: Optional[StrictStr] = Field(default=None, description="The upper limit on mortgage costs for a specific period (in pence, not GBP).")
    preference_fixed_rate: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant want to fix their mortgage costs for a certain period.")
    preference_early_year_discount: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants a discount on the mortgage repayments in the early years.")
    preference_term: Optional[StrictInt] = Field(default=None, description="Term.")
    preference_target_property: Optional[StrictStr] = Field(default=None, description="Target property.")
    preference_target_deposit: Optional[StrictStr] = Field(default=None, description="Target deposit (in pence, not GBP).")
    preference_target_property_value: Optional[StrictStr] = Field(default=None, description="Target property value (in pence, not GBP).")
    preference_cashback: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants access to an initial cash sum (Cashback).")
    preference_initial_period_months: Optional[StrictInt] = Field(default=None, description="Initial period months.")
    preference_no_early_repayment_charge: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants to have no  Early Repayment Charge on full or part repayment.")
    preference_overpayment: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants to make limited overpayments without early repayment charges.")
    preference_no_tie_in: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants to have no tie-in after a fixed, discounted or capped interest period.")
    preference_mortgage_class: Optional[CdmMortgageClass] = CdmMortgageClass.INVALID_MORTGAGE_CLASS
    preference_completion_speed_important: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant deems completion speed important.")
    preference_fee_to_loan: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants the ability to add fee to the loan.")
    preference_vary_repayment_amount: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants the ability to vary the repayment amount.")
    preference_payment_holiday: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants the ability to take payment holidays.")
    preference_link_finances: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants the ability to link your finances (e.g. bank account, savings, mortgage).")
    preference_free_conveyancing: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants to have free Conveyancing.")
    preference_free_valuations: Optional[StrictBool] = Field(default=None, description="Flag to capture if the applicant wants to have Free Valuations.")
    preference_loan_amount: Optional[StrictStr] = Field(default=None, description="Preference loan amount (in pence, not GBP). Can be undefined.")
    preference_mortgage_loan_amount: Optional[StrictStr] = Field(default=None, description="(Deprecated) The amount of the loan that is required for mortgage purposes (in pence, not GBP).  Automatically calculated as calculated_values.total_required_loan_amount.")
    funds_available_for_transaction: Optional[StrictStr] = Field(default=None, description="The sum of money the clients have available to complete their mortgage application (in pence, not GBP).")
    regulated_by: Optional[StrictStr] = Field(default=None, description="The organisation ID that regulates this case. This field cannot be set directly, and is set automatically by the backend by looking at the regulating organisation of the owner that created the case. This field cannot be updated.")
    lender_has_proposed_new_product: Optional[StrictBool] = Field(default=None, description="Indicates whether the lender has proposed a new product. This field can only be set when preference_mortgage_reason is REASON_REMORTGAGE.")
    fees: Optional[List[CdmFee]] = Field(default=None, description="Fees associated with the case. This is set automatically by the backend when the case is created. At that point in time the union of the fees on the creating user and the user's org, where the user's fee value takes precedence.")
    fee_option_title: Optional[StrictStr] = Field(default=None, description="Set of fee options chosen for the case.")
    status: Optional[CdmCaseDetailsStatus] = CdmCaseDetailsStatus.INVALID_STATUS
    change_of_preference_reason: Optional[List[StrictStr]] = Field(default=None, description="Change of preference reason. This field must be updated by appending an entry iff any case preference is being updated simultaneously.")
    flags: Optional[List[CdmCaseDetailsFlag]] = Field(default=None, description="Flags contains flags raised throughout the sourcing journey which must be reviewed.")
    recommendation_reason_for_recommendation: Optional[StrictStr] = Field(default=None, description="A String value specifying the reason for recommending the selected product. This field is optional and if provided will display under the RecommendedProduct section of the EoR document.")
    recommendation_unmet_preferences: Optional[StrictStr] = Field(default=None, description="If any preferences are unmet.")
    recommendation_term_reason: Optional[StrictStr] = Field(default=None, description="Reason for term of mortgage.")
    recommendation_reason_for_remortgage_cost_increase: Optional[StrictStr] = Field(default=None, description="Reason why the total cost of the proposed mortgage is more expensive.")
    recommendation_reason_for_recommendation_term_increase: Optional[StrictStr] = Field(default=None, description="Reason why the period of a mortgage has been increased during remortgage.")
    recommendation_strategy_for_payment_erc: Optional[StrictStr] = None
    recommendation_reason_for_equity_disparity: Optional[StrictStr] = Field(default=None, description="If OldProperty - Old Mortgage (i.e. equity from sale) > NewProperty - OldMortgage +10000 - give reason.")
    recommendation_ready_at: Optional[StrictStr] = Field(default=None, description="Recommendation ready for review. RFC3339. Cannot be set directly.")
    recommendation_document_id: Optional[StrictStr] = Field(default=None, description="Reference to generated recommendation document. Cannot be set directly.")
    recommendation_unadvised_sale: Optional[StrictBool] = Field(default=None, description="Indicates if the broker is advising the client DEFAULTS TO FALSE.")
    has_previously_owned_property: Optional[StrictBool] = None
    btl_has_lived: Optional[StrictBool] = Field(default=None, description="Has the client or a relative previously lived at the property.")
    btl_will_lived: Optional[StrictBool] = None
    btl_is_operating_a_business: Optional[StrictBool] = Field(default=None, description="Will the client or a relative be operating a business at the property.")
    notes: Optional[List[CdmCaseDetailsNote]] = Field(default=None, description="Notes associated with the case.")
    has_seen_face_to_face: Optional[StrictBool] = Field(default=None, description="Whether all client(s) on this case have been seen face-to-face by an employee of the Brokerage Firm.")
    impersonation_check_performed: Optional[StrictBool] = None
    impersonation_check_details: Optional[StrictStr] = None
    recommendation_reason_term_into_retirement: Optional[StrictStr] = Field(default=None, description="If a mortgage extends into a clients retirement, the broker must justify affordability.")
    recommendation_reason_for_affordability: Optional[StrictStr] = Field(default=None, description="For the broker to justify why the client can afford mortgage repayments.")
    recommendation_reason_additional_borrowing: Optional[StrictStr] = Field(default=None, description="If a client borrows more than their last mortgage had outstanding then justify.")
    suitability_report_recommendation_introduction: Optional[StrictStr] = Field(default=None, description="Allows broker to introduce the suitability report to provide context for the client.")
    recommendation_reason_help_to_buy_loan_repayment_strategy: Optional[StrictStr] = Field(default=None, description="Details of how the client plans to repay a help to buy loan if appropriate.")
    recommendation_reason_strategy_for_btl_void_period_coverage: Optional[StrictStr] = Field(default=None, description="If a case type is a buy-to-let, mortgage payments may not always be covered by rental income during tenancy gaps.")
    recommendation_reason_retirement_interest_only: Optional[StrictStr] = Field(default=None, description="When a interest-only mortgage is recommended, the broker must justify affordability.  DEPRECATED: due to GBP to pence conversion.  google.protobuf.UInt32Value preference_upper_limit = 8;  google.protobuf.UInt32Value preference_target_deposit = 26;  google.protobuf.UInt32Value preference_target_property_value = 27;")
    preference_tracker: Optional[StrictBool] = Field(default=None, description="Has client expressed preference for a mortgage with a rate that tracks an index.")
    last_update: Optional[StrictStr] = Field(default=None, description="The most recent case update where the recommendation was NOT invalidated. Automatically populated; cannot be set.")
    last_significant_update: Optional[StrictStr] = Field(default=None, description="The most recent update to the case where the recommendation was invalidated. Automatically populated; cannot be set.")
    protection: Optional[CdmCaseProtection] = None
    protection_quote_id: Optional[StrictStr] = None
    btl_through_company: Optional[StrictBool] = None
    is_introduced: Optional[StrictBool] = Field(default=None, description="If a case has been introduced to the broker. This value is utilised in eNBS submissions.")
    introducer_details: Optional[StrictStr] = Field(default=None, description="If a case has been introduced, this field records details of the introducer. This field is required when is_introduced is true.")
    is_debt_consolidation: Optional[StrictBool] = Field(default=None, description="If a case is for debt consolidation purposes.")
    enbs_events: Optional[List[CaseDetailsENBSEvent]] = Field(default=None, description="List of eNBS events that an eNBS message was sent to for.")
    calculated_values: Optional[CdmCaseCalculatedValues] = None
    initial_client_versions: Optional[List[CaseDetailsClientIDAndVersion]] = Field(default=None, description="List of client IDs and their version when they were added to the case.  A new version is created for each client when they're added to the case, so this version is referenceable and immutable")
    completion_client_versions: Optional[List[CaseDetailsClientIDAndVersion]] = Field(default=None, description="List of client IDs and their version when the case was completed. DEPRECATED: we now use the transition_client_versions.")
    debt_consolidation_alternative_options: Optional[StrictStr] = Field(default=None, description="Alternative options considered by the adviser before debt consolidation was advised on.")
    debt_consolidation_reason_for_consolidated_debts: Optional[StrictStr] = Field(default=None, description="Advisor's reasons for recommending client debts be consolidated.")
    initial_property_versions: Optional[List[CdmPropertyIDAndVersion]] = Field(default=None, description="List of property IDs and their version when they were added to the case.  Properties linked to case via preference_target_property or preference_related_property_sale are versioned")
    completion_property_versions: Optional[List[CdmPropertyIDAndVersion]] = Field(default=None, description="List of property IDs and their version when the case was completed. DEPRECATED: we now use the transition_property_versions.")
    initial_mortgage_versions: Optional[List[CdmMortgageIDAndVersion]] = Field(default=None, description="List of mortgage IDs and their version when they were added to the case.")
    completion_mortgage_versions: Optional[List[CdmMortgageIDAndVersion]] = Field(default=None, description="List of mortgage IDs and their version when the case was completed. DEPRECATED: we now use the transition_mortgage_versions.")
    initial_protection_versions: Optional[List[CdmProtectionIDAndVersion]] = Field(default=None, description="List of protection IDs and their version when they were added to the case.")
    transition_mortgage_versions: Optional[List[CdmTransitionMortgageVersions]] = Field(default=None, description="ID and version of the mortgages when the case was transitioning to a particular status. The snapshot is made just before the transition happens and changes are done to the entities.")
    transition_property_versions: Optional[List[CdmTransitionPropertyVersions]] = Field(default=None, description="ID and version of the properties when the case was transitioning to that status. The snapshot is made just before the transition happens and changes are done to the entities.")
    transition_client_versions: Optional[List[CdmTransitionClientVersions]] = Field(default=None, description="ID and version of the clients when the case was transitioning to that status. The snapshot is made just before the transition happens and changes are done to the entities.")
    transition_protection_versions: Optional[List[CdmTransitionProtectionVersions]] = Field(default=None, description="ID and version of the protection when the case was transitioning to that status. The snapshot is made just before the transition happens and changes are done to the entities.")
    reason_for_further_advance: Optional[StrictStr] = Field(default=None, description="The reason the client wants a Further Advance.")
    shared_budget: Optional[StrictBool] = Field(default=None, description="Used to indicate, on a Dual Fact Find case, if the budget is shared.")
    third_party_orgs: Optional[List[CdmThirdPartyOrg]] = Field(default=None, description="Details of third party organisations such as estate agent and solicitor.")
    introducer_id: Optional[StrictStr] = Field(default=None, description="If this case was introduced, the user id of the introducing user.")
    additional_loan_required: Optional[StrictBool] = Field(default=None, description="Indicates if the client requires an additional loan.")
    additional_loans: Optional[List[CdmAdditionalLoan]] = Field(default=None, description="The additional loans required.")
    override_client_eidv_results: Optional[List[CaseDetailsEidvResultOverride]] = Field(default=None, description="This field indicates the clients for which the eidv result should be ignored and it should be understood as a valid result. There can be only one instance per client.  This field can only be set if the user has the regulated/compliance/approvecases permission. And it will only be taken into account for UNIDENTIFIED results.")
    cancellation_reason: Optional[CaseDetailsCancellationReason] = CaseDetailsCancellationReason.INVALID_CANCELLATION_REASON
    conveyancing_type: Optional[CaseDetailsConveyancingType] = CaseDetailsConveyancingType.NO_SELECTION
    own_solicitor_details: Optional[CaseDetailsConveyancer] = None
    conveyancing_status: Optional[CaseDetailsConveyancingStatusType] = CaseDetailsConveyancingStatusType.INVALID_CONVEYANCING_STATUS
    referrals: Optional[List[CdmReferral]] = Field(default=None, description="Any referrals made to partners eg conveyancing, insurance.")
    related_users: Optional[List[CdmRelatedUser]] = Field(default=None, description="Users related to the case.")
    related_cases: Optional[List[CdmRelatedCase]] = Field(default=None, description="Cases related to the case.")
    external: Optional[List[CdmExternal]] = Field(default=None, description="Identifiers for the case in third-party systems.")
    lead_metadata: Optional[CaseDetailsLeadMetadata] = None
    additional_metadata: Optional[List[CdmAddnMetadata]] = Field(default=None, description="Additional metadata can be used by the FE to store additional data.")
    contact_history: Optional[List[CdmContactHistory]] = None
    exchange_date: Optional[StrictStr] = Field(default=None, description="The Date of exchange of contracts (YYYY-MM-DD).")
    conveyancing_updates: Optional[List[CaseDetailsConveyancingUpdate]] = Field(default=None, description="A list of updates we have received from the conveyancer regarding the transaction.")
    reason_for_recommendation_product_type: Optional[StrictStr] = Field(default=None, description="Reason to explain why the given product type (Fixed; Variable etc.) was chosen.")
    reason_for_recommendation_initial_term: Optional[StrictStr] = Field(default=None, description="Reason to explain why the given term was chosen.")
    survey_answers: Optional[List[CdmSurveyAnswer]] = Field(default=None, description="Survey answers contains a list of surveys answers that clients should set to finalise the survey. This should only be set after the case status transitioned to COMPLETE.")
    client_generated_lead: Optional[StrictBool] = Field(default=None, description="To mark the lead as originating from the client’s survey feedback.")
    __properties: ClassVar[List[str]] = ["case_id", "client_ids", "owner_user_id", "owner_organisation_id", "preference_mortgage_reason", "preference_related_property_sale", "preference_details", "preference_repayment_percentage", "preference_repayment_amount", "preference_interest_only_repayment_strategy", "preference_upper_limit", "preference_fixed_rate", "preference_early_year_discount", "preference_term", "preference_target_property", "preference_target_deposit", "preference_target_property_value", "preference_cashback", "preference_initial_period_months", "preference_no_early_repayment_charge", "preference_overpayment", "preference_no_tie_in", "preference_mortgage_class", "preference_completion_speed_important", "preference_fee_to_loan", "preference_vary_repayment_amount", "preference_payment_holiday", "preference_link_finances", "preference_free_conveyancing", "preference_free_valuations", "preference_loan_amount", "preference_mortgage_loan_amount", "funds_available_for_transaction", "regulated_by", "lender_has_proposed_new_product", "fees", "fee_option_title", "status", "change_of_preference_reason", "flags", "recommendation_reason_for_recommendation", "recommendation_unmet_preferences", "recommendation_term_reason", "recommendation_reason_for_remortgage_cost_increase", "recommendation_reason_for_recommendation_term_increase", "recommendation_strategy_for_payment_erc", "recommendation_reason_for_equity_disparity", "recommendation_ready_at", "recommendation_document_id", "recommendation_unadvised_sale", "has_previously_owned_property", "btl_has_lived", "btl_will_lived", "btl_is_operating_a_business", "notes", "has_seen_face_to_face", "impersonation_check_performed", "impersonation_check_details", "recommendation_reason_term_into_retirement", "recommendation_reason_for_affordability", "recommendation_reason_additional_borrowing", "suitability_report_recommendation_introduction", "recommendation_reason_help_to_buy_loan_repayment_strategy", "recommendation_reason_strategy_for_btl_void_period_coverage", "recommendation_reason_retirement_interest_only", "preference_tracker", "last_update", "last_significant_update", "protection", "protection_quote_id", "btl_through_company", "is_introduced", "introducer_details", "is_debt_consolidation", "enbs_events", "calculated_values", "initial_client_versions", "completion_client_versions", "debt_consolidation_alternative_options", "debt_consolidation_reason_for_consolidated_debts", "initial_property_versions", "completion_property_versions", "initial_mortgage_versions", "completion_mortgage_versions", "initial_protection_versions", "transition_mortgage_versions", "transition_property_versions", "transition_client_versions", "transition_protection_versions", "reason_for_further_advance", "shared_budget", "third_party_orgs", "introducer_id", "additional_loan_required", "additional_loans", "override_client_eidv_results", "cancellation_reason", "conveyancing_type", "own_solicitor_details", "conveyancing_status", "referrals", "related_users", "related_cases", "external", "lead_metadata", "additional_metadata", "contact_history", "exchange_date", "conveyancing_updates", "reason_for_recommendation_product_type", "reason_for_recommendation_initial_term", "survey_answers", "client_generated_lead"]

    model_config = ConfigDict(
        populate_by_name=True,
        validate_assignment=True,
        protected_namespaces=(),
    )


    def to_str(self) -> str:
        """Returns the string representation of the model using alias"""
        return pprint.pformat(self.model_dump(by_alias=True))

    def to_json(self) -> str:
        """Returns the JSON representation of the model using alias"""
        # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
        return json.dumps(self.to_dict())

    @classmethod
    def from_json(cls, json_str: str) -> Optional[Self]:
        """Create an instance of CdmCaseDetails from a JSON string"""
        return cls.from_dict(json.loads(json_str))

    def to_dict(self) -> Dict[str, Any]:
        """Return the dictionary representation of the model using alias.

        This has the following differences from calling pydantic's
        `self.model_dump(by_alias=True)`:

        * `None` is only added to the output dict for nullable fields that
          were set at model initialization. Other fields with value `None`
          are ignored.
        """
        excluded_fields: Set[str] = set([
        ])

        _dict = self.model_dump(
            by_alias=True,
            exclude=excluded_fields,
            exclude_none=True,
        )
        # override the default output from pydantic by calling `to_dict()` of each item in fees (list)
        _items = []
        if self.fees:
            for _item_fees in self.fees:
                if _item_fees:
                    _items.append(_item_fees.to_dict())
            _dict['fees'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in flags (list)
        _items = []
        if self.flags:
            for _item_flags in self.flags:
                if _item_flags:
                    _items.append(_item_flags.to_dict())
            _dict['flags'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in notes (list)
        _items = []
        if self.notes:
            for _item_notes in self.notes:
                if _item_notes:
                    _items.append(_item_notes.to_dict())
            _dict['notes'] = _items
        # override the default output from pydantic by calling `to_dict()` of protection
        if self.protection:
            _dict['protection'] = self.protection.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in enbs_events (list)
        _items = []
        if self.enbs_events:
            for _item_enbs_events in self.enbs_events:
                if _item_enbs_events:
                    _items.append(_item_enbs_events.to_dict())
            _dict['enbs_events'] = _items
        # override the default output from pydantic by calling `to_dict()` of calculated_values
        if self.calculated_values:
            _dict['calculated_values'] = self.calculated_values.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in initial_client_versions (list)
        _items = []
        if self.initial_client_versions:
            for _item_initial_client_versions in self.initial_client_versions:
                if _item_initial_client_versions:
                    _items.append(_item_initial_client_versions.to_dict())
            _dict['initial_client_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in completion_client_versions (list)
        _items = []
        if self.completion_client_versions:
            for _item_completion_client_versions in self.completion_client_versions:
                if _item_completion_client_versions:
                    _items.append(_item_completion_client_versions.to_dict())
            _dict['completion_client_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in initial_property_versions (list)
        _items = []
        if self.initial_property_versions:
            for _item_initial_property_versions in self.initial_property_versions:
                if _item_initial_property_versions:
                    _items.append(_item_initial_property_versions.to_dict())
            _dict['initial_property_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in completion_property_versions (list)
        _items = []
        if self.completion_property_versions:
            for _item_completion_property_versions in self.completion_property_versions:
                if _item_completion_property_versions:
                    _items.append(_item_completion_property_versions.to_dict())
            _dict['completion_property_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in initial_mortgage_versions (list)
        _items = []
        if self.initial_mortgage_versions:
            for _item_initial_mortgage_versions in self.initial_mortgage_versions:
                if _item_initial_mortgage_versions:
                    _items.append(_item_initial_mortgage_versions.to_dict())
            _dict['initial_mortgage_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in completion_mortgage_versions (list)
        _items = []
        if self.completion_mortgage_versions:
            for _item_completion_mortgage_versions in self.completion_mortgage_versions:
                if _item_completion_mortgage_versions:
                    _items.append(_item_completion_mortgage_versions.to_dict())
            _dict['completion_mortgage_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in initial_protection_versions (list)
        _items = []
        if self.initial_protection_versions:
            for _item_initial_protection_versions in self.initial_protection_versions:
                if _item_initial_protection_versions:
                    _items.append(_item_initial_protection_versions.to_dict())
            _dict['initial_protection_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in transition_mortgage_versions (list)
        _items = []
        if self.transition_mortgage_versions:
            for _item_transition_mortgage_versions in self.transition_mortgage_versions:
                if _item_transition_mortgage_versions:
                    _items.append(_item_transition_mortgage_versions.to_dict())
            _dict['transition_mortgage_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in transition_property_versions (list)
        _items = []
        if self.transition_property_versions:
            for _item_transition_property_versions in self.transition_property_versions:
                if _item_transition_property_versions:
                    _items.append(_item_transition_property_versions.to_dict())
            _dict['transition_property_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in transition_client_versions (list)
        _items = []
        if self.transition_client_versions:
            for _item_transition_client_versions in self.transition_client_versions:
                if _item_transition_client_versions:
                    _items.append(_item_transition_client_versions.to_dict())
            _dict['transition_client_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in transition_protection_versions (list)
        _items = []
        if self.transition_protection_versions:
            for _item_transition_protection_versions in self.transition_protection_versions:
                if _item_transition_protection_versions:
                    _items.append(_item_transition_protection_versions.to_dict())
            _dict['transition_protection_versions'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in third_party_orgs (list)
        _items = []
        if self.third_party_orgs:
            for _item_third_party_orgs in self.third_party_orgs:
                if _item_third_party_orgs:
                    _items.append(_item_third_party_orgs.to_dict())
            _dict['third_party_orgs'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in additional_loans (list)
        _items = []
        if self.additional_loans:
            for _item_additional_loans in self.additional_loans:
                if _item_additional_loans:
                    _items.append(_item_additional_loans.to_dict())
            _dict['additional_loans'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in override_client_eidv_results (list)
        _items = []
        if self.override_client_eidv_results:
            for _item_override_client_eidv_results in self.override_client_eidv_results:
                if _item_override_client_eidv_results:
                    _items.append(_item_override_client_eidv_results.to_dict())
            _dict['override_client_eidv_results'] = _items
        # override the default output from pydantic by calling `to_dict()` of own_solicitor_details
        if self.own_solicitor_details:
            _dict['own_solicitor_details'] = self.own_solicitor_details.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in referrals (list)
        _items = []
        if self.referrals:
            for _item_referrals in self.referrals:
                if _item_referrals:
                    _items.append(_item_referrals.to_dict())
            _dict['referrals'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in related_users (list)
        _items = []
        if self.related_users:
            for _item_related_users in self.related_users:
                if _item_related_users:
                    _items.append(_item_related_users.to_dict())
            _dict['related_users'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in related_cases (list)
        _items = []
        if self.related_cases:
            for _item_related_cases in self.related_cases:
                if _item_related_cases:
                    _items.append(_item_related_cases.to_dict())
            _dict['related_cases'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in external (list)
        _items = []
        if self.external:
            for _item_external in self.external:
                if _item_external:
                    _items.append(_item_external.to_dict())
            _dict['external'] = _items
        # override the default output from pydantic by calling `to_dict()` of lead_metadata
        if self.lead_metadata:
            _dict['lead_metadata'] = self.lead_metadata.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in additional_metadata (list)
        _items = []
        if self.additional_metadata:
            for _item_additional_metadata in self.additional_metadata:
                if _item_additional_metadata:
                    _items.append(_item_additional_metadata.to_dict())
            _dict['additional_metadata'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in contact_history (list)
        _items = []
        if self.contact_history:
            for _item_contact_history in self.contact_history:
                if _item_contact_history:
                    _items.append(_item_contact_history.to_dict())
            _dict['contact_history'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in conveyancing_updates (list)
        _items = []
        if self.conveyancing_updates:
            for _item_conveyancing_updates in self.conveyancing_updates:
                if _item_conveyancing_updates:
                    _items.append(_item_conveyancing_updates.to_dict())
            _dict['conveyancing_updates'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in survey_answers (list)
        _items = []
        if self.survey_answers:
            for _item_survey_answers in self.survey_answers:
                if _item_survey_answers:
                    _items.append(_item_survey_answers.to_dict())
            _dict['survey_answers'] = _items
        return _dict

    @classmethod
    def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
        """Create an instance of CdmCaseDetails from a dict"""
        if obj is None:
            return None

        if not isinstance(obj, dict):
            return cls.model_validate(obj)

        _obj = cls.model_validate({
            "case_id": obj.get("case_id"),
            "client_ids": obj.get("client_ids"),
            "owner_user_id": obj.get("owner_user_id"),
            "owner_organisation_id": obj.get("owner_organisation_id"),
            "preference_mortgage_reason": obj.get("preference_mortgage_reason") if obj.get("preference_mortgage_reason") is not None else CaseDetailsMortgageReason.INVALID_MORTGAGE_REASON,
            "preference_related_property_sale": obj.get("preference_related_property_sale"),
            "preference_details": obj.get("preference_details"),
            "preference_repayment_percentage": obj.get("preference_repayment_percentage"),
            "preference_repayment_amount": obj.get("preference_repayment_amount"),
            "preference_interest_only_repayment_strategy": obj.get("preference_interest_only_repayment_strategy"),
            "preference_upper_limit": obj.get("preference_upper_limit"),
            "preference_fixed_rate": obj.get("preference_fixed_rate"),
            "preference_early_year_discount": obj.get("preference_early_year_discount"),
            "preference_term": obj.get("preference_term"),
            "preference_target_property": obj.get("preference_target_property"),
            "preference_target_deposit": obj.get("preference_target_deposit"),
            "preference_target_property_value": obj.get("preference_target_property_value"),
            "preference_cashback": obj.get("preference_cashback"),
            "preference_initial_period_months": obj.get("preference_initial_period_months"),
            "preference_no_early_repayment_charge": obj.get("preference_no_early_repayment_charge"),
            "preference_overpayment": obj.get("preference_overpayment"),
            "preference_no_tie_in": obj.get("preference_no_tie_in"),
            "preference_mortgage_class": obj.get("preference_mortgage_class") if obj.get("preference_mortgage_class") is not None else CdmMortgageClass.INVALID_MORTGAGE_CLASS,
            "preference_completion_speed_important": obj.get("preference_completion_speed_important"),
            "preference_fee_to_loan": obj.get("preference_fee_to_loan"),
            "preference_vary_repayment_amount": obj.get("preference_vary_repayment_amount"),
            "preference_payment_holiday": obj.get("preference_payment_holiday"),
            "preference_link_finances": obj.get("preference_link_finances"),
            "preference_free_conveyancing": obj.get("preference_free_conveyancing"),
            "preference_free_valuations": obj.get("preference_free_valuations"),
            "preference_loan_amount": obj.get("preference_loan_amount"),
            "preference_mortgage_loan_amount": obj.get("preference_mortgage_loan_amount"),
            "funds_available_for_transaction": obj.get("funds_available_for_transaction"),
            "regulated_by": obj.get("regulated_by"),
            "lender_has_proposed_new_product": obj.get("lender_has_proposed_new_product"),
            "fees": [CdmFee.from_dict(_item) for _item in obj["fees"]] if obj.get("fees") is not None else None,
            "fee_option_title": obj.get("fee_option_title"),
            "status": obj.get("status") if obj.get("status") is not None else CdmCaseDetailsStatus.INVALID_STATUS,
            "change_of_preference_reason": obj.get("change_of_preference_reason"),
            "flags": [CdmCaseDetailsFlag.from_dict(_item) for _item in obj["flags"]] if obj.get("flags") is not None else None,
            "recommendation_reason_for_recommendation": obj.get("recommendation_reason_for_recommendation"),
            "recommendation_unmet_preferences": obj.get("recommendation_unmet_preferences"),
            "recommendation_term_reason": obj.get("recommendation_term_reason"),
            "recommendation_reason_for_remortgage_cost_increase": obj.get("recommendation_reason_for_remortgage_cost_increase"),
            "recommendation_reason_for_recommendation_term_increase": obj.get("recommendation_reason_for_recommendation_term_increase"),
            "recommendation_strategy_for_payment_erc": obj.get("recommendation_strategy_for_payment_erc"),
            "recommendation_reason_for_equity_disparity": obj.get("recommendation_reason_for_equity_disparity"),
            "recommendation_ready_at": obj.get("recommendation_ready_at"),
            "recommendation_document_id": obj.get("recommendation_document_id"),
            "recommendation_unadvised_sale": obj.get("recommendation_unadvised_sale"),
            "has_previously_owned_property": obj.get("has_previously_owned_property"),
            "btl_has_lived": obj.get("btl_has_lived"),
            "btl_will_lived": obj.get("btl_will_lived"),
            "btl_is_operating_a_business": obj.get("btl_is_operating_a_business"),
            "notes": [CdmCaseDetailsNote.from_dict(_item) for _item in obj["notes"]] if obj.get("notes") is not None else None,
            "has_seen_face_to_face": obj.get("has_seen_face_to_face"),
            "impersonation_check_performed": obj.get("impersonation_check_performed"),
            "impersonation_check_details": obj.get("impersonation_check_details"),
            "recommendation_reason_term_into_retirement": obj.get("recommendation_reason_term_into_retirement"),
            "recommendation_reason_for_affordability": obj.get("recommendation_reason_for_affordability"),
            "recommendation_reason_additional_borrowing": obj.get("recommendation_reason_additional_borrowing"),
            "suitability_report_recommendation_introduction": obj.get("suitability_report_recommendation_introduction"),
            "recommendation_reason_help_to_buy_loan_repayment_strategy": obj.get("recommendation_reason_help_to_buy_loan_repayment_strategy"),
            "recommendation_reason_strategy_for_btl_void_period_coverage": obj.get("recommendation_reason_strategy_for_btl_void_period_coverage"),
            "recommendation_reason_retirement_interest_only": obj.get("recommendation_reason_retirement_interest_only"),
            "preference_tracker": obj.get("preference_tracker"),
            "last_update": obj.get("last_update"),
            "last_significant_update": obj.get("last_significant_update"),
            "protection": CdmCaseProtection.from_dict(obj["protection"]) if obj.get("protection") is not None else None,
            "protection_quote_id": obj.get("protection_quote_id"),
            "btl_through_company": obj.get("btl_through_company"),
            "is_introduced": obj.get("is_introduced"),
            "introducer_details": obj.get("introducer_details"),
            "is_debt_consolidation": obj.get("is_debt_consolidation"),
            "enbs_events": [CaseDetailsENBSEvent.from_dict(_item) for _item in obj["enbs_events"]] if obj.get("enbs_events") is not None else None,
            "calculated_values": CdmCaseCalculatedValues.from_dict(obj["calculated_values"]) if obj.get("calculated_values") is not None else None,
            "initial_client_versions": [CaseDetailsClientIDAndVersion.from_dict(_item) for _item in obj["initial_client_versions"]] if obj.get("initial_client_versions") is not None else None,
            "completion_client_versions": [CaseDetailsClientIDAndVersion.from_dict(_item) for _item in obj["completion_client_versions"]] if obj.get("completion_client_versions") is not None else None,
            "debt_consolidation_alternative_options": obj.get("debt_consolidation_alternative_options"),
            "debt_consolidation_reason_for_consolidated_debts": obj.get("debt_consolidation_reason_for_consolidated_debts"),
            "initial_property_versions": [CdmPropertyIDAndVersion.from_dict(_item) for _item in obj["initial_property_versions"]] if obj.get("initial_property_versions") is not None else None,
            "completion_property_versions": [CdmPropertyIDAndVersion.from_dict(_item) for _item in obj["completion_property_versions"]] if obj.get("completion_property_versions") is not None else None,
            "initial_mortgage_versions": [CdmMortgageIDAndVersion.from_dict(_item) for _item in obj["initial_mortgage_versions"]] if obj.get("initial_mortgage_versions") is not None else None,
            "completion_mortgage_versions": [CdmMortgageIDAndVersion.from_dict(_item) for _item in obj["completion_mortgage_versions"]] if obj.get("completion_mortgage_versions") is not None else None,
            "initial_protection_versions": [CdmProtectionIDAndVersion.from_dict(_item) for _item in obj["initial_protection_versions"]] if obj.get("initial_protection_versions") is not None else None,
            "transition_mortgage_versions": [CdmTransitionMortgageVersions.from_dict(_item) for _item in obj["transition_mortgage_versions"]] if obj.get("transition_mortgage_versions") is not None else None,
            "transition_property_versions": [CdmTransitionPropertyVersions.from_dict(_item) for _item in obj["transition_property_versions"]] if obj.get("transition_property_versions") is not None else None,
            "transition_client_versions": [CdmTransitionClientVersions.from_dict(_item) for _item in obj["transition_client_versions"]] if obj.get("transition_client_versions") is not None else None,
            "transition_protection_versions": [CdmTransitionProtectionVersions.from_dict(_item) for _item in obj["transition_protection_versions"]] if obj.get("transition_protection_versions") is not None else None,
            "reason_for_further_advance": obj.get("reason_for_further_advance"),
            "shared_budget": obj.get("shared_budget"),
            "third_party_orgs": [CdmThirdPartyOrg.from_dict(_item) for _item in obj["third_party_orgs"]] if obj.get("third_party_orgs") is not None else None,
            "introducer_id": obj.get("introducer_id"),
            "additional_loan_required": obj.get("additional_loan_required"),
            "additional_loans": [CdmAdditionalLoan.from_dict(_item) for _item in obj["additional_loans"]] if obj.get("additional_loans") is not None else None,
            "override_client_eidv_results": [CaseDetailsEidvResultOverride.from_dict(_item) for _item in obj["override_client_eidv_results"]] if obj.get("override_client_eidv_results") is not None else None,
            "cancellation_reason": obj.get("cancellation_reason") if obj.get("cancellation_reason") is not None else CaseDetailsCancellationReason.INVALID_CANCELLATION_REASON,
            "conveyancing_type": obj.get("conveyancing_type") if obj.get("conveyancing_type") is not None else CaseDetailsConveyancingType.NO_SELECTION,
            "own_solicitor_details": CaseDetailsConveyancer.from_dict(obj["own_solicitor_details"]) if obj.get("own_solicitor_details") is not None else None,
            "conveyancing_status": obj.get("conveyancing_status") if obj.get("conveyancing_status") is not None else CaseDetailsConveyancingStatusType.INVALID_CONVEYANCING_STATUS,
            "referrals": [CdmReferral.from_dict(_item) for _item in obj["referrals"]] if obj.get("referrals") is not None else None,
            "related_users": [CdmRelatedUser.from_dict(_item) for _item in obj["related_users"]] if obj.get("related_users") is not None else None,
            "related_cases": [CdmRelatedCase.from_dict(_item) for _item in obj["related_cases"]] if obj.get("related_cases") is not None else None,
            "external": [CdmExternal.from_dict(_item) for _item in obj["external"]] if obj.get("external") is not None else None,
            "lead_metadata": CaseDetailsLeadMetadata.from_dict(obj["lead_metadata"]) if obj.get("lead_metadata") is not None else None,
            "additional_metadata": [CdmAddnMetadata.from_dict(_item) for _item in obj["additional_metadata"]] if obj.get("additional_metadata") is not None else None,
            "contact_history": [CdmContactHistory.from_dict(_item) for _item in obj["contact_history"]] if obj.get("contact_history") is not None else None,
            "exchange_date": obj.get("exchange_date"),
            "conveyancing_updates": [CaseDetailsConveyancingUpdate.from_dict(_item) for _item in obj["conveyancing_updates"]] if obj.get("conveyancing_updates") is not None else None,
            "reason_for_recommendation_product_type": obj.get("reason_for_recommendation_product_type"),
            "reason_for_recommendation_initial_term": obj.get("reason_for_recommendation_initial_term"),
            "survey_answers": [CdmSurveyAnswer.from_dict(_item) for _item in obj["survey_answers"]] if obj.get("survey_answers") is not None else None,
            "client_generated_lead": obj.get("client_generated_lead")
        })
        return _obj


