-
-
Save epochwolf/187d24af5fe3f7bc092c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'active_support/builder' unless defined?(Builder) | |
class AcordWriter | |
def initialize(policy) | |
@xml_string = "" | |
builder = ::Builder::XmlMarkup.new(target:@xml_string, indent:4) | |
build_acord(builder, policy) | |
end | |
def to_xml | |
@xml_string | |
end | |
def build_acord(xml, policy) | |
ns = { | |
"xsi:noNamespaceSchemaLocation" => "acord-pcs-v1_20_0-nodoc-codes.xsd", | |
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance" | |
} | |
xml.instruct! | |
xml.instruct!("ACORD", "ACORD" => "1.20.0") | |
xml.ACORD(ns){ | |
build_sign_on_request(xml, policy) | |
build_service_request(xml, policy) | |
build_vpi_extension(xml, policy) | |
} | |
end | |
def build_sign_on_request(xml, policy) | |
xml.comment! "Sign On Request" | |
xml.SignonRq{ | |
xml.SignonPswd{ | |
xml.CustId{ | |
xml.SPName "policyissuance.vertafore.com" | |
xml.CustLoginId "kevin_pga" | |
} | |
xml.CustPswd{ | |
xml.EncryptionTypeCd "NONE" | |
xml.Pswd "138feeb3eed9f0eaa792156e217cad1b" | |
} | |
} | |
xml.ClientDt dt(Date.today) | |
xml.CustLangPref "en-US" | |
xml.ClientApp{ | |
xml.Org "Vertafore" | |
xml.Name "PolicyBuilder" | |
xml.Version "0.1" | |
} | |
} | |
end | |
def build_service_request(xml, policy) | |
xml.comment! "Service Request" | |
xml.InsuranceSvcRq{ | |
xml.RqUID String.uuid # always a new random value, app doesn't track requests. | |
xml.tag!(policy.service_request){ | |
xml.RqUID String.uuid # always a new random value, app doesn't track requests. | |
xml.ItemIdInfo{ | |
xml.SystemId policy.guid | |
xml.comment! "OtherIdentifier is Policy Builder's primary key for this policy" | |
xml.OtherIdentifier policy.id | |
} | |
xml.TransactionRequestDt dt(Date.today) | |
xml.CurCd "USD" | |
xml.BroadLOBCd "C" # Commercial lines | |
xml.ACORDStandardVersionCd "1120" | |
build_producers(xml, policy) | |
build_insured_or_principal(xml, policy) | |
build_pc_policy(xml, policy) | |
build_locations(xml, policy) | |
build_lob(xml, policy) | |
build_comml_sublocations(xml, policy) | |
build_remarks(xml, policy) | |
} | |
} | |
end | |
def build_producers(xml, policy) | |
xml.comment! "General Agent" | |
xml.Producer { | |
xml.ItemIdInfo { | |
#xml.AgencyId '' # Comes from AIM. Do I need this? | |
xml.InsurerId policy.carrier_ga_code | |
xml.SystemId policy.general_agent_id if policy.general_agent | |
} | |
build_general_party(xml, policy.general_agent) if policy.general_agent | |
xml.ProducerInfo { | |
# xml.ContractNumber '' # What does this do? | |
# xml.PlacingOffice '' # Name for the GA? | |
xml.ProducerRoleCd 'Intermediary' | |
# xml.FieldOfficeCd '' # Similar to PlacingOffice? | |
xml.License { xml.LicensePermitNumber policy.surplus_lines_license } | |
} | |
} | |
xml.comment! "Retail Agent" | |
if agent = policy.retail_agent | |
xml.Producer { | |
xml.ItemIdInfo { | |
#xml.AgencyId '' # Comes from AIM. Do I need this? | |
xml.InsurerId agent.agent_code | |
xml.SystemId agent.id | |
} | |
build_general_party(xml, agent) | |
xml.ProducerInfo { | |
xml.ProducerRoleCd 'Agency' | |
} | |
} | |
end | |
end | |
def build_insured_or_principal(xml, policy) | |
xml.comment! "Insured" | |
if insured = policy.insured | |
xml.InsuredOrPrincipal{ | |
xml.ItemIdInfo { | |
xml.SystemId insured.id | |
} | |
build_general_party(xml, insured) | |
xml.InsuredOrPrincipalInfo { | |
xml.InsuredOrPrincipalRoleCd 'Insured' | |
xml.BusinessInfo { | |
xml.SICCd insured.sic_code | |
# xml.OperationsDesc '' # Old tag??? Does that mean don't use it? | |
} | |
} | |
} | |
end | |
end | |
def build_pc_policy(xml, policy) | |
xml.comment! "PcPolicy" | |
xml.tag!("CommlPolicy"){ | |
xml.PolicyNumber policy.number | |
xml.CompanyProductCd policy.program_code | |
xml.LOBCd policy.lob_code | |
xml.NAICCd policy.carrier_naic | |
xml.ContractTerm{ | |
xml.EffectiveDt dt policy.effective_at | |
xml.ExpirationDt dt policy.expires_at | |
} | |
xml.CurrentTermAmt{ xml.Amt policy.premium } | |
if policy.renewal_of_number.present? | |
xml.OtherOrPriorPolicy { | |
xml.PolicyCd "Prior" | |
xml.PolicyNumber policy.renewal_of_number | |
xml.LOBCd policy.lob_code | |
} | |
end | |
build_additional_interests(xml, policy.additional_interests.where(:building_id => nil)) | |
xml.MinPremInd policy.minimum_premium.blank? ? 0 : 1 | |
xml.MinPremAmt{ xml.Amt policy.minimum_premium } unless policy.minimum_premium.blank? | |
xml.CommlPolicySupplement{ | |
xml.AuditInd policy.audit_code.blank? ? 0 : 1 | |
xml.AuditFrequencyCd{ xml.Amt policy.audit_code } unless policy.audit_code.blank? | |
build_taxes_fees(xml, policy) | |
} | |
xml.CommlCoverage{ | |
xml.CoverageCd policy.terrorism_code | |
xml.CurrentTermAmt{ xml.Amt policy.terrorism_premium } if policy.terrorism_premium.present? | |
} | |
} | |
end | |
def build_additional_interests(xml, interests) | |
xml.comment! "Additional Interests" | |
interests.each do |interest| | |
xml.AdditionalInterest { | |
xml.ItemIdInfo { | |
xml.SystemId interest.id | |
} | |
build_general_party(xml, interest) | |
xml.AdditionalInterestInfo { | |
xml.NatureInterestCd interest.role | |
} | |
} | |
end | |
end | |
def build_taxes_fees(xml, policy) | |
xml.comment! "Taxes / Fees" | |
policy.tax_fees.each do |fee| | |
xml.CreditOrSurcharge{ | |
xml.CreditSurchargeCd fee.code | |
xml.CreditSurchargeAmtDesc fee.code_name | |
xml.NumericValue { xml.FormatCurrencyAmt{ xml.Amt fee.amount } } | |
xml.SecondaryCd fee.type_code if fee.type_code.present? | |
} | |
end | |
end | |
def build_locations(xml, policy) | |
xml.comment! "Locations" | |
policy.locations.each do |location| | |
xml.Location(id: location.acord_id){ | |
xml.ItemIdInfo{ | |
xml.AgencyId location.number | |
xml.SystemId location.id | |
} | |
build_address(xml, location.address) | |
location.buildings.each do |building| | |
xml.SubLocation(id: building.acord_id){ | |
xml.ItemIdInfo{ | |
xml.AgencyId building.number | |
xml.SystemId building.id | |
} | |
build_address(xml, location.address) | |
xml.SubLocationName building.name | |
build_additional_interests(xml, building.additional_interests) | |
} | |
end | |
xml.LocationName location.name | |
xml.LocationDesc location.description | |
} | |
end | |
end | |
def build_lob(xml, policy) | |
policy.gl? and build_gl(xml, policy) | |
policy.property? and build_property(xml, policy) | |
policy.crime? and build_crime(xml, policy) | |
policy.inland_marine? and build_inland_marine(xml, policy) | |
end | |
def build_gl(xml, policy) | |
xml.comment! "General Liability" | |
line = policy.lines_of_business.gl | |
xml.GeneralLiabilityLineBusiness{ | |
xml.LOBCd line.lob_code | |
xml.NAICCd policy.carrier_naic | |
build_term xml, line.premium | |
xml.LiabilityInfo{ | |
xml.CommlCoverage{ | |
xml.CoverageCd 'GL' | |
xml.CoverageDesc 'Commercial General Liability' | |
xml.comment! "Deductible" | |
xml.Deductible { # TODO: Add GL Deductible | |
build_amt xml, policy.gl_deductible.try(:amount) | |
xml.DeductibleTypeCd policy.gl_deductible.try(:type_code) | |
xml.DeductibleAppliesToCd policy.gl_deductible.try(:applies_to_code) | |
} | |
} | |
xml.comment! "Limits" | |
policy.gl_limits.each do |limit| | |
xml.CommlCoverage{ | |
xml.CoverageCd limit.code | |
xml.CoverageDesc limit.code_name | |
xml.Limit { build_amt xml, limit.amount } | |
} | |
end | |
xml.comment! "Classifications" | |
policy.gl_classifications.each do |cls| | |
xml.GeneralLiabilityClassification(building_ref(cls.building)){ | |
xml.ItemIdInfo { | |
xml.SystemId cls.id | |
} | |
xml.CommlCoverage{ | |
xml.CoverageCd "PREM" | |
build_term xml, cls.premises_premium | |
xml.Rate cls.premises_rate | |
xml.MinPremAmt cls.premises_minimum_premium | |
} | |
xml.CommlCoverage{ | |
xml.CoverageCd "PRDCO" | |
build_term xml, cls.product_premium | |
xml.Rate cls.product_rate | |
xml.MinPremAmt cls.product_minimum_premium | |
} | |
xml.ClassCd cls.code | |
xml.ClassCdDesc cls.description | |
xml.Exposure cls.exposure | |
xml.PremiumBasisCd cls.basis | |
} | |
end | |
} | |
} | |
end | |
def build_crime(xml, policy) | |
xml.comment! "Crime" | |
line = policy.lines_of_business.crime | |
xml.CrimeLineBusiness { | |
xml.LOBCd line.lob_code | |
xml.NAICCd policy.carrier_naic | |
build_term xml, line.premium | |
policy.crime_coverages.each do |c| | |
loc = building_ref(c.building) | |
xml.CrimeCoverageInfo(loc){ | |
xml.CrimePlanCd "PLAN1" | |
c.details_by_form_code.each do |form, details| | |
xml.CrimeCoverageDetails{ | |
xml.CrimeCoverageFormCd form | |
details.each do |detail| | |
xml.CommlCoverage{ | |
xml.CoverageCd detail.code | |
xml.CoverageDesc detail.description | |
xml.Limit{ | |
build_amt xml, detail.limit | |
xml.LimitAppliesToCd detail.applies_to_code | |
} | |
xml.Deductible{ | |
build_amt xml, detail.deductible | |
} | |
build_term xml, detail.premium | |
} | |
end | |
} | |
end | |
} | |
end | |
} | |
end | |
def build_property(xml, policy) | |
xml.comment! "Property" | |
line = policy.lines_of_business.property | |
xml.CommlPropertyLineBusiness { | |
xml.LOBCd line.lob_code | |
xml.NAICCd policy.carrier_naic | |
build_term xml, line.premium | |
xml.PropertyInfo { | |
policy.locations.map(&:buildings).flatten.each do |building| | |
detail = building.building_detail | |
coverages = building.property_coverages | |
next unless detail || !coverages.empty? | |
xml.CommlPropertyInfo(building_ref(building)){ | |
if detail | |
xml.ItemValueAmt{ xml.Amt detail.value } | |
xml.ClassCd detail.class_code | |
end | |
coverages.each do |coverage| | |
xml.CommlCoverage{ | |
xml.CoverageCd coverage.code | |
xml.CoverageDesc coverage.code_description | |
xml.Limit { | |
build_amt xml, coverage.limit | |
xml.ValuationCd coverage.valuation_code | |
} | |
xml.Deductible{ | |
build_amt xml, coverage.deductible | |
} | |
xml.Deductible{ | |
xml.DeductibleAppliesToCd 'EGK' | |
build_amt xml, coverage.earthquake_deductible | |
} | |
xml.Option { | |
xml.OptionCd coverage.indemnity_limit_code | |
} | |
build_term xml, coverage.premium | |
xml.Rate coverage.rate | |
xml.CommlCoverageSupplement{ | |
xml.CoverageSubCd coverage.covered_cause_of_loss_code | |
xml.CoinsurancePct coverage.coinsurance_percent | |
} | |
} | |
end | |
} | |
end | |
} | |
} | |
end | |
def build_comml_sublocations(xml, policy) | |
return unless policy.property? | |
policy.building_details.each do |detail| | |
xml.CommlSubLocation(building_ref(detail.building)){ | |
xml.InterestCd detail.interest_code | |
xml.Construction{ | |
xml.ConstructionCd detail.construction_code | |
xml.SidingCd detail.siding_code | |
xml.YearBuilt detail.year_built | |
xml.BldgArea { | |
xml.NumUnits detail.square_footage | |
xml.UnitMeasurementCd "SQFT" | |
} | |
xml.NumStories detail.number_of_stories | |
xml.NumUnits detail.number_of_units | |
xml.RoofingMaterial{ | |
xml.RoofMaterialCd detail.roofing_code | |
} | |
xml.GlassTypeCd detail.glass_type_code | |
} | |
xml.BldgProtection{ | |
xml.ProtectionClassGradeCd detail.protection_grade_code | |
xml.SprinkleredPct detail.sprinklered_percent | |
} | |
xml.BldgOccupancy{ | |
xml.VacantUnoccupiedCd 'VAC' if detail.occupancy_code == 'VAC' | |
xml.OccupiedPct detail.percent_occupied | |
xml.NatureBusinessCd detail.occupancy_code unless detail.occupancy_code == 'VAC' | |
} | |
xml.WindClassCd detail.wind_code | |
xml.SeacoastOrOtherBodyWaterProximityCd detail.water_proximity_code | |
} | |
end | |
end | |
def build_vpi_extension(xml, policy) | |
xml.comment! "Vertafore Extensions" | |
xml.VertaforeExtensions { | |
xml.VPI{ | |
xml.Version '0001' | |
xml.SLANumber policy.sla_number if policy.sla_number.present? | |
xml.Custom '' | |
xml.Contracts '' | |
xml.ExternalServices{ | |
xml.PromptingCancelUrl "http://localhost:3000/builder/#{policy.id}/summary?aborted=true" | |
xml.PromptingFinishUrl "http://localhost:3000/builder/#{policy.id}/finished" | |
} | |
} | |
} | |
end | |
def build_remarks(xml, policy) | |
#xml.Remark("This location doesn't exist.", "IdRef" => "a6de2b90-8a6a-11e1-b0c4-0800200c9a66") | |
end | |
def build_address(xml, address) | |
return unless address | |
xml.Addr{ | |
xml.AddrTypeCd Address::TYPES[address.address_type] | |
xml.Addr1 address.address1 | |
xml.Addr2 address.address2 if address.address2.present? | |
xml.City address.city | |
xml.StateProvCd address.state | |
xml.PostalCode address.zipcode | |
xml.ItemIdInfo { | |
xml.SystemId address.id | |
} | |
} | |
end | |
def build_phones(xml, object) | |
return unless object && [:fax, :phone, :email].any?{|s| object.send(s).present? } | |
xml.Communications { | |
if object.phone.present? | |
xml.PhoneInfo{ | |
xml.PhoneTypeCd "Phone" | |
xml.PhoneNumber object.phone | |
} | |
end | |
if object.fax.present? | |
xml.PhoneInfo{ | |
xml.PhoneTypeCd "Fax" | |
xml.PhoneNumber object.fax | |
} | |
end | |
if object.email.present? | |
xml.EmailInfo { | |
xml.EmailAddr object.email | |
} | |
end | |
} | |
end | |
def build_general_party(xml, object) | |
return unless object | |
xml.GeneralPartyInfo { | |
if object.name | |
xml.NameInfo { | |
xml.CommlName { | |
xml.CommercialName object.name | |
} | |
if object.respond_to?(:dba_name) && object.dba_name.present? | |
xml.SupplementaryNameInfo { | |
xml.SupplementaryNameCd 'DBA' | |
xml.SupplementaryName object.dba_name | |
} | |
end | |
xml.LegalEntityCd object.form_of_business if object.is_a? Insured | |
} | |
end | |
object.addresses.each do |address| | |
build_address(xml, address) | |
end | |
build_phones(xml, object) | |
} | |
end | |
def build_term(xml, amt) | |
xml.CurrentTermAmt { xml.Amt amt } | |
end | |
def build_amt(xml, amt) | |
xml.FormatCurrencyAmt { xml.Amt amt } | |
end | |
protected | |
def building_ref(building) | |
if building | |
{:LocationRef => building.location.acord_id, :SubLocationRef => building.acord_id} | |
else | |
{} | |
end | |
end | |
# don't want outside classes directly accessing the builder object. | |
def builder | |
@builder | |
end | |
# DateTime converter | |
def dt(date) | |
AcordConvert.acord_date date | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment