lmi-commits
[Top][All Lists]
Advanced

[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'
             ;
         }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]