Source code for q2_sdk.hq.models.account

import json
from datetime import datetime
from decimal import Decimal
from functools import cached_property
from typing import Any, Dict, List, Optional

from lxml import etree, objectify

from q2_sdk.models.recursive_encoder import RecursiveEncoder
from q2_sdk.tools.utils import to_bool, to_int
from .xml_helper import find_with_default
from .account_rights import AccountRights
from .hade import HADE
from .hydra_product import get_hydra_product_object


[docs] class Account: """ Object representation of the Account information that comes in on a Q2 Online request Typically created by passing in the XML shape provided by HQ. Also possible to build this by passing in kwargs. ie. ``Account(element: lxmlElement)`` or ``Account(CifInternal="12345")`` If both are provided, kwargs will override anything inside the given element """ def __init__( self, element: Optional[objectify.Element] = None, data_elements: Optional[List[objectify.Element]] = None, provider_data: Optional[dict] = None, **kwargs, ): """ :param element: XML node form Q2 Online request :param data_elements: Optional list of Q2_AccountDataElement nodes. :param provider_data: If the account originates from another system (such as a core), this can hold additional data that does not fit into the Q2 model. :param kwargs: Any items provided as kwargs will override the data passed in from element """ assertions = (element is not None, kwargs) assert any(assertions), ( "Either lxml Element or kwarg attributes must be specified" ) if element is None: element = objectify.Element("AccountListResponseRecord") for key, value in kwargs.items(): existing = element.find(key) if existing: element.remove(existing) element.append(objectify.fromstring(f"<{key}>{value}</{key}>")) self.data_elements = data_elements self.element = element self.provider_data = provider_data self._access = None self._host_acct_id = None self._allow_interest = None self._hade_dict = {}
[docs] @cached_property def aba(self) -> Optional[str]: return find_with_default(self.element, "Aba", data_type=str)
@property def access(self) -> Optional[int]: if self._access is None: self._access = find_with_default(self.element, "Access", data_type=int) return self._access @access.setter def access(self, value: int) -> None: self._access = to_int(value)
[docs] @cached_property def acct_desc(self) -> Optional[str]: return find_with_default(self.element, "AccountDesc", data_type=str)
[docs] @cached_property def acct_label(self) -> Optional[str]: return find_with_default(self.element, "AccountLabel", data_type=str)
[docs] @cached_property def acct_number_internal(self) -> Optional[str]: return find_with_default(self.element, "AccountNumberInternal", data_type=str)
[docs] @cached_property def acct_number_external(self) -> Optional[str]: return find_with_default(self.element, "AccountNumberExternal", data_type=str)
[docs] @cached_property def acct_number_external_unmasked(self) -> Optional[str]: return find_with_default( self.element, "AccountNumberExternalUnmasked", data_type=str )
[docs] @cached_property def acct_number_internal_unmasked(self) -> Optional[str]: return find_with_default( self.element, "AccountNumberInternalUnmasked", data_type=str )
@property def allow_interest(self) -> Optional[bool]: if not self._allow_interest: self._allow_interest = find_with_default( self.element, "AllowInterest", data_type=bool ) return self._allow_interest @allow_interest.setter def allow_interest(self, value: bool) -> None: self._allow_interest = to_bool(value)
[docs] @cached_property def allow_principal(self) -> Optional[bool]: return find_with_default(self.element, "AllowPrincipal", data_type=bool)
[docs] @cached_property def apr_or_apy(self) -> Optional[str]: return find_with_default(self.element, "APRorAPY")
[docs] @cached_property def balance1(self) -> Optional[Decimal]: return find_with_default(self.element, "Balance1", data_type=Decimal)
[docs] @cached_property def balance2(self) -> Optional[Decimal]: return find_with_default(self.element, "Balance2", data_type=Decimal)
[docs] @cached_property def balance_additional_desc1(self) -> Optional[str]: return find_with_default(self.element, "BalanceAdditionalDescription1")
[docs] @cached_property def balance_additional_desc2(self) -> Optional[str]: return find_with_default(self.element, "BalanceAdditionalDescription2")
[docs] @cached_property def balance_desc1(self) -> Optional[str]: return find_with_default(self.element, "BalanceDescription1")
[docs] @cached_property def balance_desc2(self) -> Optional[str]: return find_with_default(self.element, "BalanceDescription2")
[docs] @cached_property def balance_hade_name_to_use(self) -> Optional[str]: return find_with_default(self.element, "RunningBalanceHadeNameToUse")
[docs] @cached_property def balance_name1(self) -> Optional[str]: return find_with_default(self.element, "BalanceName1")
[docs] @cached_property def balance_name2(self) -> Optional[str]: return find_with_default(self.element, "BalanceName2")
[docs] @cached_property def balance_to_disp_tran_to_desc(self) -> Optional[str]: return find_with_default(self.element, "BalanceToDisplayTransferToDescription")
[docs] @cached_property def balance_to_display(self) -> Optional[Decimal]: return find_with_default(self.element, "BalanceToDisplay", data_type=Decimal)
[docs] @cached_property def balance_to_display_desc(self) -> Optional[str]: return find_with_default(self.element, "BalanceToDisplayDescription")
[docs] @cached_property def balance_to_display_tran_to(self) -> Optional[Decimal]: return find_with_default( self.element, "BalanceToDisplayTransferTo", data_type=Decimal )
[docs] @cached_property def balance_type1(self) -> Optional[str]: return find_with_default(self.element, "BalanceType1")
[docs] @cached_property def balance_type2(self) -> Optional[str]: return find_with_default(self.element, "BalanceType2")
[docs] @cached_property def calc_balance_for_history(self) -> Optional[bool]: return find_with_default( self.element, "CalculateRunningBalanceForyHistory", data_type=bool )
[docs] @cached_property def calc_balance_for_memos(self) -> Optional[bool]: return find_with_default( self.element, "CalculateRunningBalanceForMemos", data_type=bool )
[docs] @cached_property def cif(self) -> Optional[str]: return find_with_default(self.element, "Cif", data_type=str)
[docs] @cached_property def cif_internal(self) -> Optional[str]: cif = find_with_default(self.element, "CifInternal", data_type=str) if not cif: cif = find_with_default(self.element, "CIFInternal", data_type=str) return cif
[docs] @cached_property def cif_internal_unmasked(self) -> Optional[str]: cif = find_with_default(self.element, "CIFInternalUnmasked", data_type=str) if not cif: cif = find_with_default(self.element, "CifInternalUnmasked", data_type=str) return cif
[docs] @cached_property def cif_external(self) -> Optional[str]: cif = find_with_default(self.element, "CifExternal", data_type=str) if not cif: cif = find_with_default(self.element, "CIFExternal", data_type=str) return cif
[docs] @cached_property def cif_external_unmasked(self) -> Optional[str]: cif = find_with_default(self.element, "CIFExternalUnmasked", data_type=str) if not cif: cif = find_with_default(self.element, "CifExternalUnmasked", data_type=str) return cif
[docs] @cached_property def data_as_of_date(self) -> Optional[datetime]: return find_with_default(self.element, "DataAsOfDate", data_type=datetime)
[docs] @cached_property def default_pymt_amt_tran_from_desc(self) -> Optional[str]: return find_with_default( self.element, "DefaultPaymentAmountTransferFromDescription" )
[docs] @cached_property def default_tran_from_pymt_amt(self) -> Optional[Decimal]: return find_with_default( self.element, "DefaultPaymentAmountTransferFrom", data_type=Decimal )
[docs] @cached_property def display_order(self) -> Optional[int]: return find_with_default(self.element, "DisplayOrder", data_type=int)
[docs] @cached_property def display_running_balance(self) -> Optional[bool]: return find_with_default(self.element, "DisplayRunningBalance", data_type=bool)
[docs] @cached_property def has_pending_memos(self) -> Optional[bool]: return find_with_default(self.element, "HasPendingMemos", data_type=bool)
[docs] @cached_property def history_count(self) -> Optional[int]: return find_with_default(self.element, "HistoryCount", data_type=int)
[docs] @cached_property def history_count_type(self) -> Optional[str]: return find_with_default(self.element, "HistoryCountType")
@property def host_acct_id(self) -> Optional[int]: if not self._host_acct_id: self._host_acct_id = find_with_default( self.element, "HostAccountID", data_type=int ) return self._host_acct_id @host_acct_id.setter def host_acct_id(self, value: int) -> None: self._host_acct_id = to_int(value)
[docs] @cached_property def hydra_product_code(self) -> Optional[str]: return find_with_default(self.element, "HydraProductCode", data_type=str)
[docs] @cached_property def hydra_product_type_code(self) -> Optional[str]: return find_with_default(self.element, "HydraProductTypeCode", data_type=str)
[docs] @cached_property def is_external_acct(self) -> Optional[bool]: return find_with_default(self.element, "IsExternalAccount", data_type=bool)
[docs] @cached_property def is_partial_details(self) -> Optional[bool]: return find_with_default(self.element, "IsPartialDetails", data_type=bool)
[docs] @cached_property def masked_cif_internal(self) -> Optional[str]: return find_with_default(self.element, "MaskedCifInternal", data_type=str)
[docs] @cached_property def mob_dashboard_balance_desc(self) -> Optional[str]: return find_with_default(self.element, "MobilityDashboardBalanceDescription")
[docs] @cached_property def mobility_dashboard_balance(self) -> Optional[Decimal]: return find_with_default( self.element, "MobilityDashboardBalance", data_type=Decimal )
[docs] @cached_property def nickname(self) -> Optional[str]: return find_with_default(self.element, "NickName")
[docs] @cached_property def overview_acct_number(self) -> str: return find_with_default(self.element, "OverviewAccountNumber", default="")
[docs] @cached_property def product_id(self) -> Optional[int]: return find_with_default(self.element, "ProductID", data_type=int)
[docs] @cached_property def product_name(self) -> Optional[str]: return find_with_default(self.element, "ProductName")
[docs] @cached_property def product_type_id(self) -> Optional[int]: return find_with_default(self.element, "ProductTypeID", data_type=int)
[docs] @cached_property def product_type_name(self) -> Optional[str]: return find_with_default(self.element, "ProductTypeName")
[docs] @cached_property def product_type_voice_file(self) -> Optional[str]: return find_with_default(self.element, "ProductTypeVoiceFile")
[docs] @cached_property def product_voice_file(self) -> Optional[str]: return find_with_default(self.element, "ProductVoiceFile")
[docs] @cached_property def sort_value(self) -> Optional[str]: return find_with_default(self.element, "SortValue", default="")
[docs] @cached_property def status_short_name(self) -> Optional[str]: return find_with_default(self.element, "statusShortName")
[docs] @cached_property def user_display_order(self) -> int: return find_with_default( self.element, "UserDisplayOrder", data_type=int, default=0 )
[docs] @cached_property def user_id(self) -> Optional[int]: return find_with_default(self.element, "UserID", data_type=int)
@property def hade_dict(self) -> Dict[str, HADE]: """ Create a dict of HADE objects keyed by name for all data_elements that match the host_acct_id """ if not self._hade_dict: data_elements = self.data_elements if data_elements is None: data_elements = [] self._hade_dict = { x.HADEName: HADE(x) for x in data_elements if x.HostAccountID.text == str(self.host_acct_id) } return self._hade_dict
[docs] @cached_property def can_deposit(self) -> bool: """Return bool indicating whether this account's access level allows deposit""" return (self.access & AccountRights.Deposit) == AccountRights.Deposit
[docs] @cached_property def can_view(self) -> bool: """Return bool indicating whether this account's access level allows view""" return (self.access & AccountRights.View) == AccountRights.View
[docs] @cached_property def can_withdraw(self) -> bool: """Return bool indicating whether this account's access level allows withdrawal""" return (self.access & AccountRights.Withdraw) == AccountRights.Withdraw
[docs] @cached_property def hydra_product_name(self) -> str: """Returns a generic product name for the account""" hydra_product = get_hydra_product_object( self.hydra_product_type_code, self.hydra_product_code ) return hydra_product.name
[docs] @staticmethod def from_json(data) -> "Account": element = objectify.fromstring(data["element"]) data_elements = data.get("data_elements") if data_elements is not None: data_elements = [objectify.fromstring(x) for x in data_elements] provider_data = data.get("provider_data") if isinstance(provider_data, str): provider_data = json.loads(provider_data) account = Account( element, provider_data=provider_data, data_elements=data_elements ) return account
[docs] def to_json(self) -> Dict[str, Any]: return json.loads(RecursiveEncoder().encode(self))
def __json__(self): return { "element": etree.tostring(self.element).decode(), "provider_data": json.dumps(self.provider_data), "data_elements": [etree.tostring(x).decode() for x in self.data_elements], } def __repr__(self) -> Optional[str]: return "<Object Account: {}>".format(self.host_acct_id)