[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [5945] Read and write new input and output formats
From: |
Greg Chicares |
Subject: |
[lmi-commits] [5945] Read and write new input and output formats |
Date: |
Sun, 21 Sep 2014 15:22:27 +0000 |
Revision: 5945
http://svn.sv.gnu.org/viewvc/?view=rev&root=lmi&revision=5945
Author: chicares
Date: 2014-09-21 15:22:26 +0000 (Sun, 21 Sep 2014)
Log Message:
-----------
Read and write new input and output formats
Modified Paths:
--------------
lmi/trunk/ChangeLog
lmi/trunk/custom_io_1.cpp
Modified: lmi/trunk/ChangeLog
===================================================================
--- lmi/trunk/ChangeLog 2014-09-18 15:18:34 UTC (rev 5944)
+++ lmi/trunk/ChangeLog 2014-09-21 15:22:26 UTC (rev 5945)
@@ -34122,3 +34122,8 @@
illustration_view.cpp
Fix defect introduced 20140916T1707Z: if no custom input, show GUI.
+20140921T1522Z <address@hidden> [533]
+
+ custom_io_1.cpp
+Read and write new input and output formats.
+
Modified: lmi/trunk/custom_io_1.cpp
===================================================================
--- lmi/trunk/custom_io_1.cpp 2014-09-18 15:18:34 UTC (rev 5944)
+++ lmi/trunk/custom_io_1.cpp 2014-09-21 15:22:26 UTC (rev 5945)
@@ -29,21 +29,19 @@
#include "custom_io_1.hpp"
#include "alert.hpp"
-#include "assert_lmi.hpp"
+#include "calendar_date.hpp"
#include "configurable_settings.hpp"
-#include "database.hpp"
-#include "dbnames.hpp"
#include "et_vector.hpp"
#include "input.hpp"
#include "ledger.hpp"
#include "ledger_invariant.hpp"
#include "ledger_variant.hpp"
-#include "name_value_pairs.hpp"
#include "platform_dependent.hpp" // access()
#include "value_cast.hpp"
-#include "yare_input.hpp"
+#include "xml_lmi.hpp"
#include <fstream>
+#include <stdexcept>
#include <vector>
bool custom_io_1_file_exists()
@@ -56,7 +54,23 @@
namespace
{
-// Empty for the moment.
+ std::string fetch(xml::element const& parent, std::string const& name)
+ {
+ return xml_lmi::get_content(*xml_lmi::retrieve_element(parent, name));
+ }
+
+ // Convert a date, given in a particular format, to a JDN string.
+ // position: 0123456789_ignored_
+ // format: 12/31/1987-00:00:00 (ignore time of day)
+ std::string convert_date(std::string const& s)
+ {
+ calendar_date const z
+ (value_cast<int>(s.substr(6, 4)) // year
+ ,value_cast<int>(s.substr(0, 2)) // month
+ ,value_cast<int>(s.substr(3, 2)) // day
+ );
+ return value_cast<std::string>(z.julian_day_number());
+ }
} // Unnamed namespace.
/// Read custom input for a particular customer.
@@ -78,261 +92,186 @@
;
}
- // Always use the current declared rate.
- z["UseCurrentDeclaredRate"] = "Yes";
+ xml_lmi::dom_parser parser(actual_filename);
+ xml::element const& root = parser.root_node("lmi");
- warning() << "Testing: simulation of reading custom input." << LMI_FLUSH;
+ xml::element Benefit = *xml_lmi::retrieve_element(root, "Benefit");
- return true;
-#if 0
- name_value_pairs n_v_pairs(actual_filename);
+ xml::element Bank = *xml_lmi::retrieve_element(Benefit, "Bank"
);
+ xml::element Applicant = *xml_lmi::retrieve_element(Benefit, "Applicant"
);
+ xml::element BenefitData = *xml_lmi::retrieve_element(Benefit,
"BenefitData");
+ // Unused: <InterestRateData>
+ xml::element Agent = *xml_lmi::retrieve_element(Benefit, "Agent"
);
- // The list is not complete; other items may be required eventually.
- z["InforceYear"] =
n_v_pairs.string_numeric_value("InforceYear");
- z["InforceMonth"] =
n_v_pairs.string_numeric_value("InforceMonth");
- z["InforceGeneralAccountValue"] =
n_v_pairs.string_numeric_value("InforceAVGenAcct");
- z["InforceSeparateAccountValue"] =
n_v_pairs.string_numeric_value("InforceAVSepAcct");
- z["InforceRegularLoanValue"] =
n_v_pairs.string_numeric_value("InforceAVRegLn");
- z["InforcePreferredLoanValue"] =
n_v_pairs.string_numeric_value("InforceAVPrfLn");
- z["InforceCumulativeNoLapsePremium"] =
n_v_pairs.string_numeric_value("InforceCumNoLapsePrem");
- z["InforceCumulativeNoLapsePayments"]=
n_v_pairs.string_numeric_value("InforceCumPmts");
+ // Unused: <ProcessType>
+ std::string AutoClose = fetch(root ,
"AutoClose");
-// TRICKY !! Other input methods distinguish the insured's first, middle,
-// and last names. This method uses a single field to meet customer
-// requirements. Combining that single field with the middle and last
-// names works as long as we initialize the others to a nonempty string.
- z["InsuredName"] = n_v_pairs.string_value("ApplicantName");
-// Not yet used, but might be wanted someday:
-// n_v_pairs.string_value("ApplicantDOB"); // ApplicantDOB=01/01/1968
- z["IssueAge"] =
n_v_pairs.string_numeric_value("ApplicantIssueAge");
- z["RetirementAge"] = "100";
+ // Unused: <BenefitId>
+ // Unused: <BranchID>
- std::string gender = n_v_pairs.string_value("ApplicantGender");
- if("F" == gender)
- {
- z["Gender"] = "Female";
- }
- else if("M" == gender)
- {
- z["Gender"] = "Male";
- }
- else if("U" == gender)
- {
- z["Gender"] = "Unisex";
- }
- else
- {
- fatal_error()
- << "ApplicantGender is '"
- << gender
- << "', but it must be 'F', 'M', or 'U'."
- << LMI_FLUSH
- ;
- }
+ // Unused: <BankFDIC>
+ std::string BankName = fetch(Bank , "BankName"
);
+ std::string BankAddress = fetch(Bank ,
"BankAddress");
+ std::string BankCity = fetch(Bank , "BankCity"
);
+ std::string BankState = fetch(Bank , "BankState"
);
+ std::string BankZip = fetch(Bank , "BankZip"
);
- std::string tobacco = n_v_pairs.string_value("ApplicantTobacco");
- if("Y" == tobacco)
- {
- z["Smoking"] = "Smoker";
- }
- else if("N" == tobacco)
- {
- z["Smoking"] = "Nonsmoker";
- }
- else if("U" == tobacco)
- {
- z["Smoking"] = "Unismoke";
- }
- else
- {
- fatal_error()
- << "ApplicantTobacco is '"
- << tobacco
- << "', but it must be 'Y', 'N', or 'U'."
- << LMI_FLUSH
- ;
- }
+ std::string ApplicantFirstName = fetch(Applicant ,
"ApplicantFirstName" );
+ std::string ApplicantLastName = fetch(Applicant ,
"ApplicantLastName" );
+ // Unused: <ApplicantSSN>
+ std::string ApplicantDOB = fetch(Applicant ,
"ApplicantDOB" );
+ // Unused: <ApplicantIssueAge>
+ std::string ApplicantGender = fetch(Applicant ,
"ApplicantGender" );
+ std::string ApplicantTobacco = fetch(Applicant ,
"ApplicantTobacco" );
+ // Unused: <ApplicantHomeState>
+ std::string ApplicantState = fetch(Applicant ,
"ApplicantState" );
+ std::string ApplicantRating = fetch(Applicant ,
"ApplicantRating" );
+ // Unused: <ApplicantRatingThruAge>
+ std::string ApplicantPermFlatExtraAmt = fetch(Applicant ,
"ApplicantPermFlatExtraAmt" );
+ std::string ApplicantTempFlatExtraAmt = fetch(Applicant ,
"ApplicantTempFlatExtraAmt" );
+ std::string ApplicantTempFlatExtraThruAge = fetch(Applicant ,
"ApplicantTempFlatExtraThruAge");
- z["StateOfJurisdiction"] = n_v_pairs.string_value("ApplicantState");
- z["PremiumTaxState"] = n_v_pairs.string_value("ApplicantState");
+ // Unused: <PaymentsPerYear>
+ std::string ProductCode = fetch(BenefitData,
"ProductCode" );
+ // Unused: <ProductOption>
+ std::string DeathBenefitOption = fetch(BenefitData,
"DeathBenefitOption");
+ // Unused: <LoadOption>
+ std::string FaceAmt = fetch(BenefitData, "FaceAmt"
);
+ std::string PremiumAmt = fetch(BenefitData,
"PremiumAmt" );
+ std::string ExchangeAmt = fetch(BenefitData,
"ExchangeAmt" );
+ // Unused: <PremiumYears>
+ // Unused: <Revised>
+ // Unused: <DefinitionOfLifeInsurance>
+ std::string WireDate = fetch(BenefitData, "WireDate"
);
+ // Unused: <Mortality>
+ // Unused: <CompReduction>
+ std::string Underwriting = fetch(BenefitData,
"Underwriting" );
+ // Unused: <HedgedRate>
-// Not yet used, but might be wanted someday:
-// PaymentsPerYear=1
+ // Unused: <InterestRateData> (all subelements)
- z["ProductName"] = n_v_pairs.string_value("ProductCode");
+ std::string AgentName = fetch(Agent , "AgentName"
);
+ std::string AgentAddress = fetch(Agent ,
"AgentAddress");
+ std::string AgentCity = fetch(Agent , "AgentCity"
);
+ std::string AgentState = fetch(Agent ,
"AgentState" );
+ std::string AgentZip = fetch(Agent , "AgentZip"
);
+ std::string AgentPhone = fetch(Agent ,
"AgentPhone" );
+ std::string AgentLicense = fetch(Agent ,
"AgentLicense");
+ // Unused: <AgentCompanyName>
- if("Standard" != z["UnderwritingClass"].str())
- {
- fatal_error()
- << "Internal error: not initialized to standard rate class."
- << LMI_FLUSH
- ;
- }
+ // For certain fields, empty strings are taken to imply default values.
+ if(ApplicantRating .empty()) ApplicantRating = "None";
+ if(ApplicantPermFlatExtraAmt.empty()) ApplicantPermFlatExtraAmt = "0.0";
+ if(ApplicantTempFlatExtraAmt.empty()) ApplicantTempFlatExtraAmt = "0.0";
- std::string undw = n_v_pairs.string_value("ProductOption");
- if("P" == undw)
- {
- z["UnderwritingClass"] = "Preferred";
- z["GroupUnderwritingType"] = "Medical";
- }
- else if("F" == undw)
- {
- z["GroupUnderwritingType"] = "Medical";
- }
- else if("S" == undw)
- {
- z["GroupUnderwritingType"] = "Simplified issue";
- }
- else if("G" == undw)
- {
- z["GroupUnderwritingType"] = "Guaranteed issue";
- }
- else
- {
- fatal_error()
- << "ProductOption is '"
- << undw
- << "', but it must be 'P', 'F', 'S', or 'G'."
- << LMI_FLUSH
- ;
- }
+ // Always use the current declared rate; disregard <InterestRateData>.
+ z["UseCurrentDeclaredRate"] = "Yes";
- std::string dbopt = n_v_pairs.string_value("DeathBenefitOption");
- if("L" == dbopt)
- {
- z["DeathBenefitOption"] = "a";
- }
- else if("I" == dbopt)
- {
- z["DeathBenefitOption"] = "b";
- }
- else if("ROP" == dbopt)
- {
- z["DeathBenefitOption"] = "rop";
- }
- else
- {
- fatal_error()
- << "DeathBenefitOption is '"
- << dbopt
- << "', but it must be 'L', 'I', or 'ROP'."
- << LMI_FLUSH
- ;
- }
+ // Always calculate issue age from DOB. <ApplicantDOB> is always
+ // specified. This is robust: lmi knows exactly how to calculate
+ // insurance age for every product it supports, using ALB or ANB
+ // as the case may be; and age changes are handled automatically.
+ z["UseDOB" ] = "Yes";
- // For single-premium cases, the specified amount would normally
- // be calculated by using a "corridor" specified-amount strategy,
- // but the customer wants to enter the specified amount explicitly.
- z["SpecifiedAmount"] = n_v_pairs.string_value("FaceAmt");
+ // Allow issue age to exceed default retirement age.
+ z["RetireesCanEnroll" ] = "Yes";
- // Zero out any default er premium.
- z["CorporationPayment"] = "0";
- // Assume single premium. Although the corporation pays it,
- // treat it, contrary to fact, as paid by the insured; reason:
- // consistency with GUI--see ChangeLog for 20050825T0122Z .
- // SOMEDAY !! Revisit this later.
- z["Payment"] = n_v_pairs.string_value("PremiumAmt") +
";0";
+ // <ApplicantState> specifies the state of jurisdiction. It might
+ // seem that <BankState> would be better for BOLI, but that would
+ // not be right for an out-of-state trust. The client system
+ // actually has an "Insurance State" GUI field, which it maps to
+ // <ApplicantState>; it doesn't identify a distinct premium-tax
+ // state, so <ApplicantState> is used for that purpose as well.
+ z["StateOfJurisdiction" ] = ApplicantState;
+ z["PremiumTaxState" ] = ApplicantState;
-// Not yet used, but might be wanted someday:
-//ExchangeAmt=0
-//PremiumYears=01 [single premium assumed for now]
-//Revised=N
-//Mortality=C
+ z["CorporationName" ] = BankName;
+ z["CorporationAddress" ] = BankAddress;
+ z["CorporationCity" ] = BankCity;
+ z["CorporationState" ] = BankState;
+ z["CorporationZipCode" ] = BankZip;
-// Table ratings: not yet used, but might be wanted someday:
-// ApplicantRating=
-// ApplicantThruAge=
- z["SubstandardTable"] = n_v_pairs.string_value("ApplicantRating");
-
- double permanent_flat =
n_v_pairs.numeric_value("PermFlatExtraAmt");
- double temporary_flat =
n_v_pairs.numeric_value("TempFlatExtraAmt");
- double temporary_flat_max_age =
n_v_pairs.numeric_value("TempFlatExtraThruAge");
- if(z.issue_age() < temporary_flat_max_age)
- {
- z["FlatExtra"] =
- value_cast<std::string>(permanent_flat + temporary_flat)
- + "[0, @"
- + value_cast<std::string>(temporary_flat_max_age)
- + "); " // Apparently this ')' should be ']' for "ThruAge".
- + value_cast<std::string>(permanent_flat)
- ;
- }
-
+ bool need_space = !ApplicantFirstName.empty() &&
!ApplicantLastName.empty();
+ std::string space(need_space, ' ');
+ z["InsuredName"] = ApplicantFirstName + space + ApplicantLastName;
+ z["DateOfBirth" ] = convert_date(ApplicantDOB);
+ z["Gender"] =
+ ("F" == ApplicantGender) ? "Female"
+ : ("M" == ApplicantGender) ? "Male"
+ : ("U" == ApplicantGender) ? "Unisex"
+ : throw std::runtime_error(ApplicantGender + ": ApplicantGender not in
{F,M,U}.")
+ ;
+ z["Smoking"] =
+ ("Y" == ApplicantTobacco) ? "Smoker"
+ : ("N" == ApplicantTobacco) ? "Nonsmoker"
+ : ("U" == ApplicantTobacco) ? "Unismoke"
+ : throw std::runtime_error(ApplicantTobacco + ": ApplicantTobacco not
in {Y,N,U}.")
+ ;
+ z["State" ] = ApplicantState;
+ z["SubstandardTable" ] = ApplicantRating; // ?! Need to know
range of values.
+ double permanent_flat =
value_cast<double>(ApplicantPermFlatExtraAmt);
+ double temporary_flat =
value_cast<double>(ApplicantTempFlatExtraAmt);
+ int temporary_flat_max_age = value_cast<int
>(ApplicantTempFlatExtraThruAge);
+ z["FlatExtra"] =
+ value_cast<std::string>(permanent_flat + temporary_flat)
+ + " [0, @"
+ + value_cast<std::string>(temporary_flat_max_age)
+ + "]; "
+ + value_cast<std::string>(permanent_flat)
+ ;
+ z["ProductName" ] = ProductCode;
+ z["DeathBenefitOption"] =
+ ("L" == DeathBenefitOption) ? "a"
+ : ("I" == DeathBenefitOption) ? "b"
+// : ("ROP" == DeathBenefitOption) ? "rop" // Generally not offered for
BOLI.
+ : throw std::runtime_error(DeathBenefitOption + ": DeathBenefitOption
not in {L,I}.")
+ ;
+ // <FaceAmt> and <PremiumAmt> are both specified, so that both can
+ // be rounded in reasonable ways--even for single-premium products
+ // that normally use a "corridor" specified-amount strategy.
+ z["SpecifiedAmount" ] = FaceAmt;
+ // Assume single premium, paid by bank.
+ z["CorporationPayment" ] = PremiumAmt + "; 0.0";
+ // Zero out default ee payment.
+ z["Payment" ] = "0.0";
+ // Assume that any 1035 exchange is external.
+ z["External1035ExchangeAmount" ] = ExchangeAmt;
+ // Assume that any 1035 exchange is from a MEC (for single-premium BOLI).
+ z["External1035ExchangeFromMec"] = "Yes";
+ z["EffectiveDate" ] = convert_date(WireDate);
+ z["GroupUnderwritingType"] =
+ "SI" == Underwriting ? "Simplified issue"
+ : "GI" == Underwriting ? "Guaranteed issue"
+ : "UW" == Underwriting ? "Medical" // ?! Does "UW" mean "underwritten"?
+ : throw std::runtime_error(Underwriting + ": Underwriting not in
{SI,GI}.")
+ ;
+ z["UnderwritingClass"] = "Standard"; // Default.
if("None" != z["SubstandardTable"].str())
{
z["UnderwritingClass"] = "Rated";
}
-
- // The current declared rate isn't necessarily used: see function
- // adjust_interest_rates() and its documentation.
- z["UseCurrentDeclaredRate"] = "No";
-
- yare_input const yip(z);
- product_database database(yip);
-
- double first_year_general_account_rate =
- 0.01
- * n_v_pairs.numeric_value("InterestRateFirstYr")
- ;
- double renewal_year_general_account_rate =
- 0.01
- * n_v_pairs.numeric_value("InterestRateOngoing")
- ;
-
- std::vector<double> declared_rate;
- database.Query(declared_rate, DB_MaxGenAcctRate);
- z["GeneralAccountRate"] = adjust_interest_rates
- (first_year_general_account_rate
- ,renewal_year_general_account_rate
- ,declared_rate
- );
-
-// Reenable this line to test the interest calculation when changing it.
-// This doesn't merit a formal, permanent unit test for now.
-// test_adjust_interest_rates();
-
-// TRICKY !! Other input methods distinguish the agent's first, middle,
-// and last names. This method uses a single field to meet customer
-// requirements. Combining that single field with the middle and last
-// names works only as long as we initialize the latter to a nonempty
-// string, which we do as a temporary workaround elsewhere; when that's
-// resolved, revisit this.
- z["AgentName"] = n_v_pairs.string_value("AgentName");
- z["AgentAddress"] = n_v_pairs.string_value("AgentAddress");
- z["AgentCity"] = n_v_pairs.string_value("AgentCity");
- z["AgentState"] = n_v_pairs.string_value("AgentState");
- z["AgentZipCode"] = n_v_pairs.string_value("AgentZip");
- z["AgentPhone"] = n_v_pairs.string_value("AgentPhone");
- z["AgentId"] = n_v_pairs.string_value("AgentLicense");
-// Not yet used, but might be wanted someday:
-// AgentCompanyName
-// AgentLicense
-
- double separate_account_rate =
- 0.01
- * n_v_pairs.numeric_value("InterestRateSepAcctFirstYr")
- ;
-
- z["SeparateAccountRate"] =
value_cast<std::string>(separate_account_rate);
-
- // TRICKY !! We need to consider the unconverted string: if it's empty,
- // it should be ignored, and must not be incorrectly converted to
- // zero. Yet one might actually wish to set the multiplier to zero;
- // that would be indicated by non-empty input evaluating to zero.
- std::string coi_mult = n_v_pairs.string_value("COIMult");
- if(!coi_mult.empty())
+ else if("PF" == ApplicantRating)
{
- z["OverrideCoiMultiplier"] = "Yes";
- z["CountryCoiMultiplier"] = coi_mult;
+ z["UnderwritingClass"] = "Preferred"; // ?! Are there any other values?
}
+ z["AgentName" ] = AgentName;
+ z["AgentAddress" ] = AgentAddress;
+ z["AgentCity" ] = AgentCity;
+ z["AgentState" ] = AgentState;
+ z["AgentZipCode" ] = AgentZip;
+ z["AgentPhone" ] = AgentPhone;
+ z["AgentId" ] = AgentLicense;
- // "AutoClose": "Y" or "N". Either way, read the custom input file
- // and write the custom output file. Then:
- // if "Y", then exit;
- // else, leave the GUI active.
- // Ignored for command-line regression testing.
- return "Y" == n_v_pairs.string_value("AutoClose");
-#endif // 0
+ // Meaning of return code based on "AutoClose":
+ // if "N", then write both PDF and custom output file;
+ // else, write custom output file only.
+ // (Originally, "Y" meant that lmi should close automatically,
+ // without displaying its GUI, and "N" meant that it should show
+ // its GUI without closing automatically. In practice, "N" was
+ // used only when it was desired to print a PDF file--which is
+ // the opposite of the current sense. Now, the GUI is never
+ // displayed, and lmi always closes automatically.)
+ return "N" == AutoClose;
}
/// Write custom output for a particular customer.
@@ -376,8 +315,8 @@
LedgerVariant const& Curr_ = ledger_values.GetCurrFull();
os
- << "CashValu,SurrValu,DeathBen,IntEarned,"
- << "MortCost,Load,MinPrem,SurrCost,PremAmt,IntRate\n"
+ << "CashValu,SurrValu,DeathBen,IntRate,IntEarned,"
+ << "MortCost,MiscFees,Load,MinPrem,SurrCost,PremAmt\n"
;
std::vector<double> surr_chg(Invar.GetLength());
@@ -396,16 +335,14 @@
<< Curr_.AcctVal [j]
<< ',' << Curr_.CSVNet [j]
<< ',' << Curr_.EOYDeathBft [j]
+ << ',' << Curr_.AnnGAIntRate [j] * 10000.0 // 'IntRate' in bp.
<< ',' << Curr_.NetIntCredited [j]
<< ',' << Curr_.COICharge [j]
-// Column headers suggest that 'Load' should precede 'MinPrem',
-// but this order was accepted; perhaps both were always zero
-// in actual practice.
- << ',' << 0 // 'MinPrem' always
zero.
+ << ',' << 0 // 'MiscFees' always
zero.
<< ',' << prem_load [j]
+ << ',' << 0 // 'MinPrem' always
zero.
<< ',' << surr_chg [j]
<< ',' << Invar.GrossPmt [j]
- << ',' << Curr_.AnnGAIntRate [j] * 10000.0 // 'IntRate' in bp.
<< '\n'
;
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [lmi-commits] [5945] Read and write new input and output formats,
Greg Chicares <=