# coding: utf-8

"""
    Zuora API Reference

    REST API reference for the Zuora Billing, Payments, and Central Platform! Check out the [REST API Overview](https://www.zuora.com/developer/api-references/api/overview/).

    The version of the OpenAPI document: 2024-05-20
    Contact: docs@zuora.com
    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, StrictFloat, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List, Optional, Union
from typing_extensions import Annotated
from zuora_sdk.models.create_payment_debit_memo_application import CreatePaymentDebitMemoApplication
from zuora_sdk.models.create_payment_invoice_application import CreatePaymentInvoiceApplication
from zuora_sdk.models.gateway_options import GatewayOptions
from zuora_sdk.models.payment_request_finance_information import PaymentRequestFinanceInformation
from zuora_sdk.models.payment_schedule_payment_option_fields import PaymentSchedulePaymentOptionFields
from zuora_sdk.models.payment_with_custom_rates import PaymentWithCustomRates
from typing import Optional, Set
from typing_extensions import Self

class CreatePaymentRequest(BaseModel):
    """
    CreatePaymentRequest
    """ # noqa: E501
    # avoid validation
    def __init__(self, **kwargs):
        # Directly assign values without validation using `construct()`
        model = self.construct(**kwargs)
        self.__dict__.update(model.__dict__)

        _dict_all = convert_snake_dict_to_camel(kwargs, self.model_fields, self.__properties)

        _dict = self.to_alias_dict(_dict_all)
        for field_name, field_info in self.model_fields.items():
            alias = field_info.alias or field_name
            if alias in _dict:
                self.__dict__[field_name] = _dict.get(alias)
        self.update_additional_properties(self, _dict_all)
        pass

    def __setattr__(self, name, value):
        # Override setattr to bypass validation when setting attributes
        object.__setattr__(self, name, value)

    account_id: Optional[StrictStr] = Field(default=None, description="The ID of the customer account that the payment is created for. ", alias="accountId")
    account_number: Optional[StrictStr] = Field(default=None, description="The number of the customer account that the payment is created for, such as `A00000001`.  You can specify either `accountNumber` or `accountId` for a customer account. If both of them are specified, they must refer to the same customer account. ", alias="accountNumber")
    amount: Union[StrictFloat, StrictInt] = Field(description="The total amount of the payment. ")
    auth_transaction_id: Optional[Annotated[str, Field(strict=True, max_length=50)]] = Field(default=None, description="The authorization transaction ID from the payment gateway. Use this field for electronic payments, such as credit cards.  When you create a payment for capturing the authorized funds, it is highly recommended to pass in the gatewayOrderId that you used when authorizing the funds by using the [Create authorization](https://www.zuora.com/developer/api-references/api/operation/Post_CreateAuthorization) operation, together with the `authTransactionId` field.  The following payment gateways support this field:   - Adyen Integration v2.0   - CyberSource 1.28   - CyberSource 1.97   - CyberSource 2.0   - Chase Paymentech Orbital   - Ingenico ePayments   - SlimPay   - Stripe v2   - Verifi Global Payment Gateway   - WePay Payment Gateway Integration ", alias="authTransactionId")
    comment: Optional[Annotated[str, Field(min_length=0, strict=True, max_length=255)]] = Field(default=None, description="Additional information related to the payment. ")
    currency: StrictStr = Field(description="When Standalone Payment is not enabled, the `currency` of the payment must be the same as the payment currency defined in the customer account settings through Zuora UI.  When Standalone Payment is enabled and `standalone` is `true`, the `currency` of the standalone payment can be different from the payment currency defined in the customer account settings. The amount will not be summed up to the account balance or key metrics regardless of currency. ")
    custom_rates: Optional[Annotated[List[PaymentWithCustomRates], Field(max_length=2)]] = Field(default=None, description="It contains Home currency and Reporting currency custom rates currencies. The maximum number of items is 2 (you can pass the Home currency item or Reporting currency item or both).  **Note**: The API custom rate feature is permission controlled. ", alias="customRates")
    debit_memos: Optional[List[CreatePaymentDebitMemoApplication]] = Field(default=None, description="Container for debit memos. The maximum number of debit memos is 1,000. ", alias="debitMemos")
    effective_date: Optional[StrictStr] = Field(default=None, description="The date when the payment takes effect, in `yyyy-mm-dd` format.  **Note:**   - This field is required for only electronic payments. It's an optional field for external payments.   - When specified, this field must be set to the date of today. ", alias="effectiveDate")
    finance_information: Optional[PaymentRequestFinanceInformation] = Field(default=None, alias="financeInformation")
    gateway_id: Optional[StrictStr] = Field(default=None, description="The ID of the gateway instance that processes the payment. The ID must be a valid gateway instance ID and this gateway must support the specific payment method.   - When creating electronic payments, this field is required.  - When creating external payments, this field is optional. ", alias="gatewayId")
    payment_gateway_number: Optional[StrictStr] = Field(default=None, description="The natural key for the payment gateway. ", alias="paymentGatewayNumber")
    gateway_options: Optional[GatewayOptions] = Field(default=None, alias="gatewayOptions")
    gateway_order_id: Optional[Annotated[str, Field(strict=True, max_length=50)]] = Field(default=None, description="A merchant-specified natural key value that can be passed to the electronic payment gateway when a payment is created. If not specified, the payment number will be passed in instead.  Gateways check duplicates on the gateway order ID to ensure that the merchant do not accidentally enter the same transaction twice. This ID can also be used to do reconciliation and tie the payment to a natural key in external systems. The source of this ID varies by merchant. Some merchants use their shopping cart order IDs, and others use something different. Merchants use this ID to track transactions in their eCommerce systems.  When you create a payment for capturing the authorized funds, it is highly recommended to pass in the gatewayOrderId that you used when authorizing the funds by using the [Create authorization](https://www.zuora.com/developer/api-references/api/operation/Post_CreateAuthorization) operation, together with the `authTransactionId` field. ", alias="gatewayOrderId")
    invoices: Optional[List[CreatePaymentInvoiceApplication]] = Field(default=None, description="Container for invoices. The maximum number of invoices is 1,000. ")
    mit_transaction_source: Optional[StrictStr] = Field(default=None, alias="mitTransactionSource")
    payment_method_id: Optional[StrictStr] = Field(default=None, description="The unique ID of the payment method that the customer used to make the payment.   If no payment method ID is specified in the request body, the default payment method for the customer account is used automatically. If the default payment method is different from the type of payments that you want to create, an error occurs. ", alias="paymentMethodId")
    payment_method_type: Optional[StrictStr] = Field(default=None, description="The type of the payment method that the customer used to make the payment.   Specify this value when you are creating an external payment method. If both `paymentMethodType` and `paymentMethodId` are specified, only the `paymentMethodId` value is used to create the payment. ", alias="paymentMethodType")
    payment_option: Optional[List[PaymentSchedulePaymentOptionFields]] = Field(default=None, description="Container for the paymentOption items, which describe the transactional level rules for processing payments. Currently, only the Gateway Options type is supported.  Here is an example: ``` \"paymentOption\": [   {     \"type\": \"GatewayOptions\",     \"detail\": {       \"SecCode\":\"WEB\"     }   } ] ```  `paymentOption` of the payment schedule takes precedence over `paymentOption` of the payment schedule item.  You can use this field or the `gatewayOptions` field to pass the Gateway Options fields supported by a payment gateway. However, the Gateway Options fields passed through the `paymentOption` field will be stored in the Payment Option object and can be easily retrieved.  To enable this field, submit a request at [Zuora Global Support](https://support.zuora.com/). This field is only available if `zuora-version` is set to 337.0 or later. ", alias="paymentOption")
    payment_schedule_key: Optional[StrictStr] = Field(default=None, description="The unique ID or the number of the payment schedule to be linked with the payment. See [Link payments to payment schedules](https://knowledgecenter.zuora.com/Billing/Billing_and_Payments/Payment_Schedules/Link_payments_with_payment_schedules) for more information.", alias="paymentScheduleKey")
    prepayment: Optional[StrictBool] = Field(default=None, description="Indicates whether the payment will be used as a reserved payment. See [Prepaid Cash with Drawdown](https://knowledgecenter.zuora.com/Zuora_Billing/Billing_and_Invoicing/JA_Advanced_Consumption_Billing/Prepaid_Cash_with_Drawdown) for more information. ")
    reference_id: Optional[Annotated[str, Field(min_length=0, strict=True, max_length=100)]] = Field(default=None, description="The transaction ID returned by the payment gateway. Use this field to reconcile payments between your gateway and Zuora Payments. ", alias="referenceId")
    soft_descriptor: Optional[Annotated[str, Field(strict=True, max_length=35)]] = Field(default=None, description="A payment gateway-specific field that maps to Zuora for the gateways, Orbital, Vantiv and Verifi.", alias="softDescriptor")
    soft_descriptor_phone: Optional[Annotated[str, Field(strict=True, max_length=20)]] = Field(default=None, description="A payment gateway-specific field that maps to Zuora for the gateways, Orbital, Vantiv and Verifi.", alias="softDescriptorPhone")
    standalone: Optional[StrictBool] = Field(default=None, description="This field is only available if support for standalone payments is enabled.  Specify `true` to create a standalone payment that will be processed in Zuora through Zuora gateway integration but will be settled outside of Zuora.  When `standalone` is set to `true`:   - `accountId`, `amount`, `currency`, and `type` are required.    - `type` must be `Electronic`.   - `currency` of the payment can be different from the payment currency in the customer account settings.   - The amount will not be summed up into the account balance and key metrics regardless of the payment currency.   - No settlement data will be created.   - Either the applied amount or the unapplied amount of the payment is zero.   - The standalone payment cannot be applied, unapplied, or transferred.  Specify `false` to create an ordinary payment that will be created, processed, and settled in Zuora. The `currency` of an ordinary payment must be the same as the currency in the customer account settings. ")
    type: StrictStr
    additional_properties: Dict[str, Any] = {}
    __properties: ClassVar[List[str]] = ["accountId", "accountNumber", "amount", "authTransactionId", "comment", "currency", "customRates", "debitMemos", "effectiveDate", "financeInformation", "gatewayId", "paymentGatewayNumber", "gatewayOptions", "gatewayOrderId", "invoices", "mitTransactionSource", "paymentMethodId", "paymentMethodType", "paymentOption", "paymentScheduleKey", "prepayment", "referenceId", "softDescriptor", "softDescriptorPhone", "standalone", "type"]

    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 CreatePaymentRequest 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.
        * Fields in `self.additional_properties` are added to the output dict.
        """
        excluded_fields: Set[str] = set([
            "additional_properties",
        ])

        _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 custom_rates (list)
        _items = []
        if self.custom_rates:
            for _item_custom_rates in self.custom_rates:
                if _item_custom_rates:
                    _items.append(_item_custom_rates.to_dict())
            _dict['customRates'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in debit_memos (list)
        _items = []
        if self.debit_memos:
            for _item_debit_memos in self.debit_memos:
                if _item_debit_memos:
                    _items.append(_item_debit_memos.to_dict())
            _dict['debitMemos'] = _items
        # override the default output from pydantic by calling `to_dict()` of finance_information
        if self.finance_information:
            _dict['financeInformation'] = self.finance_information.to_dict()
        # override the default output from pydantic by calling `to_dict()` of gateway_options
        if self.gateway_options:
            _dict['gatewayOptions'] = self.gateway_options.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in invoices (list)
        _items = []
        if self.invoices:
            for _item_invoices in self.invoices:
                if _item_invoices:
                    _items.append(_item_invoices.to_dict())
            _dict['invoices'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in payment_option (list)
        _items = []
        if self.payment_option:
            for _item_payment_option in self.payment_option:
                if _item_payment_option:
                    _items.append(_item_payment_option.to_dict())
            _dict['paymentOption'] = _items
        # puts key-value pairs in additional_properties in the top level
        if self.additional_properties is not None:
            for _key, _value in self.additional_properties.items():
                _dict[_key] = _value

        return _dict

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

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

        obj = convert_snake_dict_to_camel(obj, cls.model_fields, cls.__properties)

        _obj = cls.model_validate(cls.to_alias_dict(obj))
        return cls.update_additional_properties(_obj, obj)

    @classmethod
    def update_additional_properties(cls, obj, _dict: Optional[Dict[str, Any]]):
        # store additional fields in additional_properties

        # store additional fields in additional_properties
        for _key in _dict.keys():
            if _key not in cls.__properties:
                obj.additional_properties[_key] = _dict.get(_key)


        return obj
        pass

    @classmethod
    def to_alias_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:

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

        return {
            "accountId": obj.get("accountId"),
            "accountNumber": obj.get("accountNumber"),
            "amount": obj.get("amount"),
            "authTransactionId": obj.get("authTransactionId"),
            "comment": obj.get("comment"),
            "currency": obj.get("currency"),
            "customRates": [PaymentWithCustomRates.from_dict(_item) for _item in obj["customRates"]] if obj.get("customRates") is not None else None,
            "debitMemos": [CreatePaymentDebitMemoApplication.from_dict(_item) for _item in obj["debitMemos"]] if obj.get("debitMemos") is not None else None,
            "effectiveDate": obj.get("effectiveDate"),
            "financeInformation": PaymentRequestFinanceInformation.from_dict(obj["financeInformation"]) if obj.get("financeInformation") is not None else None,
            "gatewayId": obj.get("gatewayId"),
            "paymentGatewayNumber": obj.get("paymentGatewayNumber"),
            "gatewayOptions": GatewayOptions.from_dict(obj["gatewayOptions"]) if obj.get("gatewayOptions") is not None else None,
            "gatewayOrderId": obj.get("gatewayOrderId"),
            "invoices": [CreatePaymentInvoiceApplication.from_dict(_item) for _item in obj["invoices"]] if obj.get("invoices") is not None else None,
            "mitTransactionSource": obj.get("mitTransactionSource"),
            "paymentMethodId": obj.get("paymentMethodId"),
            "paymentMethodType": obj.get("paymentMethodType"),
            "paymentOption": [PaymentSchedulePaymentOptionFields.from_dict(_item) for _item in obj["paymentOption"]] if obj.get("paymentOption") is not None else None,
            "paymentScheduleKey": obj.get("paymentScheduleKey"),
            "prepayment": obj.get("prepayment"),
            "referenceId": obj.get("referenceId"),
            "softDescriptor": obj.get("softDescriptor"),
            "softDescriptorPhone": obj.get("softDescriptorPhone"),
            "standalone": obj.get("standalone"),
            "type": obj.get("type")
        }
        return _obj


def convert_snake_dict_to_camel(_dict: dict, model_fields, properties):
    if not isinstance(_dict, dict):
        return _dict
    new_dict = {}
    for k, v in _dict.items():
        if k in model_fields:
            # model_fields: key is attribute name like bill_to_contact,
            alias = model_fields.get(k).alias or k
            new_dict[alias] = v
            pass
        else:
            new_key = snake_to_camel(k)
            if properties is not None and isinstance(properties, list) and new_key in properties:
                new_dict[new_key] = v
            else:
                new_dict[k] = v
    return new_dict
    pass


def snake_to_camel(name):
    if name is None or '_' not in name:
        return name
    components = name.split('_')
    return components[0] + ''.join(x.title() for x in components[1:])
