from enum import Enum
from ace.core.basic_functions import classic_round
[docs]
class StopOption(Enum):
FIXED_NUMBER_YEAR = 1
AGE_BASED_TERM = 2
TERM_UNTIL_MATURITY = 3
[docs]
class Gender(Enum):
MALE = 1
FEMALE = 2
[docs]
@staticmethod
def from_char(value: str) -> 'Gender':
match value:
case "M":
return Gender.MALE
case "F":
return Gender.FEMALE
case _:
raise ValueError(f"Gender {value} does not exists")
[docs]
class UnderwritingClass(Enum):
STANDARD = 1
PREFERRED = 2
STANDARD_PLUS = 6
PREFERRED_PLUS = 19
[docs]
@staticmethod
def from_string(value: str) -> 'UnderwritingClass':
match value.upper():
case "STANDARD":
return UnderwritingClass.STANDARD
case "PREFERRED":
return UnderwritingClass.PREFERRED
case "STANDARD_PLUS":
return UnderwritingClass.STANDARD_PLUS
case "PREFERRED_PLUS" | "OPTIMUM":
return UnderwritingClass.PREFERRED_PLUS
case _:
raise ValueError(f"UnderwritingClass {value} does not exists")
[docs]
class TobaccoPremiumBasis(Enum):
NON_SMOKER = 1
SMOKER = 2
[docs]
def is_smoking(self):
return self == TobaccoPremiumBasis.SMOKER
[docs]
@staticmethod
def from_string(value: str) -> 'TobaccoPremiumBasis':
match value.upper():
case "NS":
return TobaccoPremiumBasis.NON_SMOKER
case "SM":
return TobaccoPremiumBasis.SMOKER
case _:
raise ValueError(f"TobaccoPremiumBasis {value} does not exists")
[docs]
class CoverageType(Enum):
# The order of the coverage type here is the order they will be processed
BASE_COVERAGE = 4
ADDITIONAL_TERM_INSURANCE = 8
CHILD_TERM_RIDER = 22
ACCIDENTAL_DEATH_BENEFIT = 34
TOTAL_DISABILITY_WAIVER = 65
PAYOR_DEATH_AND_DISABILITY_WAIVER = 13
OWNER_WAIVER_DEATH = 48
OWNER_WAIVER_DISABILITY = 49
OWNER_WAIVER_DEATH_AND_DISABILITY = 50
[docs]
def get_order_index(self) -> int:
return coverage_type_order_dict[self]
[docs]
def is_rider(self) -> bool:
return self != CoverageType.BASE_COVERAGE
[docs]
def is_waiver(self) -> bool:
return self in [
CoverageType.TOTAL_DISABILITY_WAIVER,
CoverageType.PAYOR_DEATH_AND_DISABILITY_WAIVER,
CoverageType.OWNER_WAIVER_DEATH,
CoverageType.OWNER_WAIVER_DISABILITY,
CoverageType.OWNER_WAIVER_DEATH_AND_DISABILITY,
]
coverage_type_order_dict = {coverageType: index for index, coverageType in enumerate(list(CoverageType))}
[docs]
class AgeCalculationType(Enum):
AGE_NEXT_BIRTHDAY = 1
AGE_LAST_BIRTHDAY = 2
AGE_NEAREST_IN_MONTHS = 4
[docs]
class ProductType(Enum):
TERM = 0
[docs]
class ProductSubType(Enum):
TERM_SIT = 0,
TERM100 = 1,
TERM_SWITCH = 2,
YOURTERM = 3,
[docs]
def get_product_type(self) -> ProductType:
if self in [
ProductSubType.TERM_SIT,
ProductSubType.TERM100,
ProductSubType.TERM_SWITCH,
ProductSubType.YOURTERM
]:
return ProductType.TERM
raise NotImplementedError()
[docs]
class ProductCode(Enum):
TERM_SIT = "RBCI_TERM_SIT"
TERM_SIT_HO = "RBCI_TERM_SIT_HO"
TERM_TERM100 = "RBCI_TERM_TERM100"
TERM_TERM100_BROKER = "RBCI_TERM_TERM100_BROKER"
TERM_TERM100_HO = "RBCI_TERM_TERM100_HO"
TERM_TERMSWITCH = "RBCI_TERM_TERMSWITCH"
TERM_TERMSWITCH_BROKER = "RBCI_TERM_TERMSWITCH_BROKER"
TERM_TERMSWITCH_HO = "RBCI_TERM_TERMSWITCH_HO"
TERM_YOURTERM = "RBCI_TERM_YOURTERM"
TERM_YOURTERM_BROKER = "RBCI_TERM_YOURTERM_BROKER"
TERM_YOURTERM_HO = "RBCI_TERM_YOURTERM_HO"
[docs]
def get_product_type(self) -> ProductType:
return self.get_product_sub_type().get_product_type()
[docs]
def get_product_sub_type(self) -> ProductSubType:
if self in [
ProductCode.TERM_SIT,
ProductCode.TERM_SIT_HO
]:
return ProductSubType.TERM_SIT
elif self in [
ProductCode.TERM_TERM100,
ProductCode.TERM_TERM100_BROKER,
ProductCode.TERM_TERM100_HO
]:
return ProductSubType.TERM100
elif self in [
ProductCode.TERM_TERMSWITCH,
ProductCode.TERM_TERMSWITCH_BROKER,
ProductCode.TERM_TERMSWITCH_HO
]:
return ProductSubType.TERM_SWITCH
elif self in [
ProductCode.TERM_YOURTERM,
ProductCode.TERM_YOURTERM_BROKER,
ProductCode.TERM_YOURTERM_HO
]:
return ProductSubType.YOURTERM
raise NotImplementedError()
[docs]
class ModalFactor(Enum):
"""
modal factors that are used to adjust the base premium for different payment frequencies.
"""
ANNUAL = 1.00
MONTHLY = 0.09
[docs]
def apply_for_array_with_rounding(self, values: list[float], number_of_digits: int = 2):
return [classic_round(self.apply(value), number_of_digits) for value in values]
[docs]
def apply(self, value: float) -> float:
return value * self.value
[docs]
def revert(self, value: float) -> float:
return value / self.value
[docs]
class Frequency(Enum):
"""
number of times a payment occurs in a year.
"""
ANNUAL = 1
MONTHLY = 12
[docs]
def get_payment_mode(self) -> "PaymentMode":
match self:
case Frequency.ANNUAL:
return PaymentMode.ANNUAL
case Frequency.MONTHLY:
return PaymentMode.MONTHLY
[docs]
def get_modal_factor(self) -> ModalFactor:
match self:
case Frequency.ANNUAL:
return ModalFactor.ANNUAL
case Frequency.MONTHLY:
return ModalFactor.MONTHLY
raise ValueError(f"No modal factor for {self}")
[docs]
class PaymentMode(Enum):
ANNUAL = 1
MONTHLY = 4
[docs]
def get_frequency(self) -> Frequency:
match self:
case PaymentMode.ANNUAL:
return Frequency.ANNUAL
case PaymentMode.MONTHLY:
return Frequency.MONTHLY
[docs]
class LifeClass(Enum):
OPTIMUM_NON_SMOKER = "Opt NS"
PREFERRED_NON_SMOKER = "Pref NS"
STANDARD_NON_SMOKER = "Std NS"
PREFERRED_SMOKER = "Pref SM"
STANDARD_SMOKER = "Std SM"
[docs]
@staticmethod
def get_from_string(life_class: str) -> 'LifeClass':
match life_class:
case "Opt NS":
return LifeClass.OPTIMUM_NON_SMOKER
case "Pref NS" | "Prf NS":
return LifeClass.PREFERRED_NON_SMOKER
case "Std NS":
return LifeClass.STANDARD_NON_SMOKER
case "Pref SM" | "Prf SM":
return LifeClass.PREFERRED_SMOKER
case "Std SM":
return LifeClass.STANDARD_SMOKER
[docs]
@staticmethod
def get_life_class(underwriting_class: UnderwritingClass, smoking_status: TobaccoPremiumBasis) -> 'LifeClass':
if underwriting_class == UnderwritingClass.STANDARD:
if smoking_status == TobaccoPremiumBasis.NON_SMOKER:
return LifeClass.STANDARD_NON_SMOKER
if smoking_status == TobaccoPremiumBasis.SMOKER:
return LifeClass.STANDARD_SMOKER
if underwriting_class == UnderwritingClass.PREFERRED:
if smoking_status == TobaccoPremiumBasis.NON_SMOKER:
return LifeClass.PREFERRED_NON_SMOKER
if smoking_status == TobaccoPremiumBasis.SMOKER:
return LifeClass.PREFERRED_SMOKER
if underwriting_class == UnderwritingClass.PREFERRED_PLUS:
if smoking_status == TobaccoPremiumBasis.NON_SMOKER:
return LifeClass.OPTIMUM_NON_SMOKER
raise ValueError(f"No life class for {underwriting_class} and {smoking_status}")
# TODO: find a better name / no abbreviations
[docs]
class IllusTxnCode(Enum):
"""
Holds the constants for different IllustrationTxn primary and secondary codes
"""
PRIMARY_TAX_BRACKET = 2
PRIMARY_SCHEDULED_DEPOSIT = 3
PRIMARY_DISTRIBUTION = 5
PRIMARY_SPECIFIED_COVERAGE_AMOUNT = 7
SECONDARY_SPECIFIED_AMOUNT = 20
SECONDARY_SPECIFIED_PREMIUM = 30
SECONDARY_SCHEDULED_LOAN_AMOUNT = 53
SECONDARY_INTEREST_RATE = 80
SECONDARY_SPECIFIED_AMOUNT_OF_TOTAL_COVERAGE = 137
[docs]
class SolveType(Enum):
SOLVE_PREMIUM = 0
SOLVE_COVERAGE = 1
[docs]
class LivesType(Enum):
SINGLE = 1
JOINT_FIRST_TO_DIE = 2
JOINT_LAST_TO_DIE = 3
[docs]
def is_joint(self) -> bool:
return self in [
LivesType.JOINT_FIRST_TO_DIE,
LivesType.JOINT_LAST_TO_DIE,
]
[docs]
class JointAgeType(Enum):
JOINT_EXACT = 1
JOINT_FRASIER = 2
JOINT_EQUIVALENT = 3
JOINT_SINGLE = 4 # ESA
[docs]
class DiscountType(Enum):
TERM_CI_BUNDLE = "RBCI_TermCI_Bundle"
COLLEAGUES_AS_CLIENTS_SAVINGS = "RBCI_Clients_Savings"
TERM_RENEWAL_SAVINGS_PROGRAM = "RBCI_Term_Renewal_Savings"
CONVERSION_CARRYOVER = "RBCI_Conversion_Carryover"