Skip to content

tags: - examples - business-rules - insurance - calculations - tutorial


Business Rules

This guide demonstrates complex, real-world business rule implementations in EverSharp, combining multiple language concepts to solve practical problems.

Insurance Premium Calculation

Basic Premium with Risk Factors

Calculate insurance premium based on multiple risk factors:

function calculateAutoPremium(driver, vehicle, coverage) {
  // Base premium
  basePremium =
    coverage.liability + coverage.collision + coverage.comprehensive;

  // Age factor
  if (driver.age < 25) {
    ageFactor = 1.5;
  } else if (driver.age < 30) {
    ageFactor = 1.2;
  } else if (driver.age < 65) {
    ageFactor = 1.0;
  } else {
    ageFactor = 1.3;
  }

  // Driving record factor
  if (driver.accidents > 2) {
    recordFactor = 1.8;
  } else if (driver.accidents > 0) {
    recordFactor = 1.3;
  } else {
    recordFactor = 0.9; // Clean record discount
  }

  // Vehicle factor
  if (vehicle.year >= 2020) {
    vehicleFactor = 1.1; // Newer cars cost more
  } else if (vehicle.year >= 2010) {
    vehicleFactor = 1.0;
  } else {
    vehicleFactor = 0.8; // Older cars cost less
  }

  // Calculate final premium
  premium = basePremium * ageFactor * recordFactor * vehicleFactor;

  return {
    "basePremium": basePremium,
    "ageFactor": ageFactor,
    "recordFactor": recordFactor,
    "vehicleFactor": vehicleFactor,
    "totalPremium": $Round(premium * 100) / 100
  };
}

// Example usage
driver = {
  "age": 28,
  "accidents": 0,
  "yearsLicensed": 10
};

vehicle = {
  "make": "Toyota",
  "model": "Camry",
  "year": 2022
};

coverage = {
  "liability": 500,
  "collision": 300,
  "comprehensive": 200
};

result = calculateAutoPremium(driver, vehicle, coverage);
// Result: {
//   basePremium: 1000,
//   ageFactor: 1.2,
//   recordFactor: 0.9,
//   vehicleFactor: 1.1,
//   totalPremium: 1188
// }

Multi-Policy Discount

Calculate discounts for multiple policies:

function calculateMultiPolicyDiscount(policies) {
  // Count active policies
  activeCount = policies.Filter((p) => p.status == "Active").Length;

  // Determine discount rate
  if (activeCount >= 4) {
    discountRate = 0.2; // 20% for 4+ policies
  } else if (activeCount >= 3) {
    discountRate = 0.15; // 15% for 3 policies
  } else if (activeCount >= 2) {
    discountRate = 0.1; // 10% for 2 policies
  } else {
    discountRate = 0; // No discount for 1 policy
  }

  // Calculate total premium before and after discount
  totalPremium = policies
    .Filter((p) => p.status == "Active")
    .Map((p) => p.premium)
    .ReduceToNum((sum, p) => sum + p);

  discountAmount = totalPremium * discountRate;
  finalPremium = totalPremium - discountAmount;

  return {
    "policyCount": activeCount,
    "totalPremium": totalPremium,
    "discountRate": discountRate,
    "discountAmount": discountAmount,
    "finalPremium": finalPremium
  };
}

policies = [
  { "id": 1, "type": "Auto", "premium": 1200, "status": "Active" },
  { "id": 2, "type": "Home", "premium": 1500, "status": "Active" },
  { "id": 3, "type": "Life", "premium": 800, "status": "Active" }
];

discount = calculateMultiPolicyDiscount(policies);
// Result: {
//   policyCount: 3,
//   totalPremium: 3500,
//   discountRate: 0.15,
//   discountAmount: 525,
//   finalPremium: 2975
// }

Eligibility Rules

Age-Based Eligibility

Complex eligibility check with multiple criteria:

function checkPolicyEligibility(applicant, policyType) {
  errors = [];

  // Calculate age
  age = $DifferenceInYears(applicant.birthDate, $Today());

  // Age requirements vary by policy type
  if (policyType == "Auto") {
    if (age < 16) {
      errors.Push("Must be at least 16 years old for auto insurance");
    }
  } else if (policyType == "Life") {
    if (age < 18) {
      errors.Push("Must be at least 18 years old for life insurance");
    } else if (age > 80) {
      errors.Push("Maximum age for new life insurance is 80");
    }
  } else if (policyType == "Home") {
    if (age < 18) {
      errors.Push("Must be at least 18 years old for home insurance");
    }
  }

  // Credit score requirement
  if (applicant.creditScore < 600) {
    errors.Push("Credit score must be at least 600");
  }

  // Income requirement for high-value policies
  if (policyType == "Life" && applicant.requestedCoverage > 1000000) {
    if (applicant.annualIncome < 100000) {
      errors.Push("Income requirement not met for coverage over $1M");
    }
  }

  // Existing policy conflicts
  if (applicant.hasActivePolicyOfType) {
    errors.Push("Active policy of this type already exists");
  }

  return {
    "eligible": errors.Length == 0,
    "age": age,
    "errors": errors
  };
}

applicant = {
  "name": "John Smith",
  "birthDate": $ToDate("1990-05-15"),
  "creditScore": 720,
  "annualIncome": 85000,
  "requestedCoverage": 500000,
  "hasActivePolicyOfType": false
};

eligibility = checkPolicyEligibility(applicant, "Life");
// Result: { eligible: true, age: 34, errors: [] }

Geographic Risk Assessment

Determine risk level based on location:

function assessGeographicRisk(address, policyType) {
  // High-risk zip codes (example data)
  highRiskZips = ["90001", "90002", "90003"];

  // Flood zones
  floodZones = ["Zone A", "Zone AE", "Zone VE"];

  riskFactors = [];
  riskMultiplier = 1.0;

  // Check zip code risk
  isHighRiskZip = highRiskZips.Any((zip) => zip == address.zipCode);
  if (isHighRiskZip) {
    riskFactors.Push("High-risk zip code");
    riskMultiplier = riskMultiplier * 1.3;
  }

  // Check flood zone
  if (policyType == "Home") {
    isFloodZone = floodZones.Any((zone) => zone == address.floodZone);
    if (isFloodZone) {
      riskFactors.Push("Flood zone");
      riskMultiplier = riskMultiplier * 1.5;
    }
  }

  // Urban vs rural
  if (address.population > 100000) {
    riskFactors.Push("Urban area");
    if (policyType == "Auto") {
      riskMultiplier = riskMultiplier * 1.2;
    }
  }

  // Determine risk level
  if (riskMultiplier >= 1.5) {
    riskLevel = "High";
  } else if (riskMultiplier >= 1.2) {
    riskLevel = "Medium";
  } else {
    riskLevel = "Low";
  }

  return {
    "riskLevel": riskLevel,
    "riskMultiplier": riskMultiplier,
    "riskFactors": riskFactors
  };
}

address = {
  "street": "123 Main St",
  "city": "Los Angeles",
  "zipCode": "90001",
  "floodZone": "Zone X",
  "population": 250000
};

risk = assessGeographicRisk(address, "Auto");
// Result: {
//   riskLevel: "High",
//   riskMultiplier: 1.56,
//   riskFactors: ["High-risk zip code", "Urban area"]
// }

Claims Processing

Claim Validation

Validate claim against policy terms:

function validateClaim(claim, policy) {
  errors = [];
  warnings = [];

  // Check policy is active
  if (policy.status != "Active") {
    errors.Push("Policy is not active");
  }

  // Check claim date is within policy period
  isAfterStart =
    $DifferenceInDays(policy.effectiveDate, claim.incidentDate) >= 0;
  isBeforeEnd =
    $DifferenceInDays(claim.incidentDate, policy.expirationDate) >= 0;

  if (!isAfterStart || !isBeforeEnd) {
    errors.Push("Incident date outside policy coverage period");
  }

  // Check claim amount against coverage limit
  if (claim.amount > policy.coverageLimit) {
    errors.Push("Claim amount exceeds coverage limit");
  }

  // Check deductible
  if (claim.amount <= policy.deductible) {
    warnings.Push("Claim amount does not exceed deductible");
  }

  // Check for duplicate claims
  daysSinceLastClaim = $DifferenceInDays(
    policy.lastClaimDate,
    claim.incidentDate
  );
  if (daysSinceLastClaim < 30) {
    warnings.Push("Multiple claims within 30 days - requires review");
  }

  // Calculate payout
  if (errors.Length == 0) {
    payableAmount = claim.amount - policy.deductible;
    if (payableAmount < 0) {
      payableAmount = 0;
    }
    if (payableAmount > policy.coverageLimit) {
      payableAmount = policy.coverageLimit;
    }
  } else {
    payableAmount = 0;
  }

  return {
    "valid": errors.Length == 0,
    "payableAmount": payableAmount,
    "errors": errors,
    "warnings": warnings
  };
}

policy = {
  "id": 12345,
  "status": "Active",
  "effectiveDate": $ToDate("2024-01-01"),
  "expirationDate": $ToDate("2024-12-31"),
  "coverageLimit": 50000,
  "deductible": 1000,
  "lastClaimDate": $ToDate("2024-03-15")
};

claim = {
  "id": 67890,
  "incidentDate": $ToDate("2024-06-15"),
  "amount": 5000,
  "description": "Vehicle collision"
};

validation = validateClaim(claim, policy);
// Result: {
//   valid: true,
//   payableAmount: 4000,
//   errors: [],
//   warnings: []
// }

Claims History Impact

Calculate rate increase based on claims history:

function calculateRateAdjustment(policy, claims) {
  // Filter claims to last 3 years
  cutoffDate = $AddYears($Today(), -3);
  recentClaims = claims.Filter(
    (c) => $DifferenceInDays(cutoffDate, c.date) >= 0
  );

  // Count claims
  claimCount = recentClaims.Length;

  // Sum claim amounts
  totalClaimed = recentClaims
    .Map((c) => c.amount)
    .ReduceToNum((sum, amt) => sum + amt);

  // Determine rate adjustment
  if (claimCount == 0) {
    adjustment = 0.95; // 5% discount for no claims
    reason = "No claims discount";
  } else if (claimCount == 1 && totalClaimed < 2000) {
    adjustment = 1.0; // No change
    reason = "Single small claim";
  } else if (claimCount == 1) {
    adjustment = 1.15; // 15% increase
    reason = "One claim";
  } else if (claimCount == 2) {
    adjustment = 1.3; // 30% increase
    reason = "Multiple claims";
  } else {
    adjustment = 1.5; // 50% increase
    reason = "Excessive claims";
  }

  newPremium = policy.currentPremium * adjustment;

  return {
    "currentPremium": policy.currentPremium,
    "adjustment": adjustment,
    "newPremium": $Round(newPremium * 100) / 100,
    "claimCount": claimCount,
    "totalClaimed": totalClaimed,
    "reason": reason
  };
}

policy = {
  "id": 12345,
  "currentPremium": 1200
};

claims = [
  { "date": $ToDate("2023-06-15"), "amount": 3000 },
  { "date": $ToDate("2024-02-20"), "amount": 1500 }
];

adjustment = calculateRateAdjustment(policy, claims);
// Result: {
//   currentPremium: 1200,
//   adjustment: 1.30,
//   newPremium: 1560,
//   claimCount: 2,
//   totalClaimed: 4500,
//   reason: "Multiple claims"
// }

Payment Processing

Payment Schedule Calculation

Generate payment schedule for installments:

function calculatePaymentSchedule(totalAmount, startDate, frequency, term) {
  // Calculate payment amount
  if (frequency == "Monthly") {
    paymentCount = term;
  } else if (frequency == "Quarterly") {
    paymentCount = term / 3;
  } else if (frequency == "Semi-Annual") {
    paymentCount = term / 6;
  } else {
    // Annual
    paymentCount = 1;
  }

  paymentAmount = totalAmount / paymentCount;
  paymentAmount = $Round(paymentAmount * 100) / 100;

  // Calculate increment in months
  if (frequency == "Monthly") {
    increment = 1;
  } else if (frequency == "Quarterly") {
    increment = 3;
  } else if (frequency == "Semi-Annual") {
    increment = 6;
  } else {
    // Annual
    increment = 12;
  }

  // Generate schedule
  schedule = [];
  currentDate = startDate;
  index = 1;

  while (index <= paymentCount) {
    payment = {
      "paymentNumber": index,
      "dueDate": currentDate,
      "amount": paymentAmount,
      "status": "Pending"
    };

    schedule.Push(payment);
    currentDate = $AddMonths(currentDate, increment);
    index = index + 1;
  }

  // Adjust last payment for rounding
  totalScheduled = paymentAmount * paymentCount;
  difference = totalAmount - totalScheduled;

  if (difference != 0) {
    lastPayment = schedule[schedule.Length - 1];
    lastPayment.amount = lastPayment.amount + difference;
  }

  return {
    "totalAmount": totalAmount,
    "frequency": frequency,
    "paymentAmount": paymentAmount,
    "paymentCount": paymentCount,
    "schedule": schedule
  };
}

result = calculatePaymentSchedule(1200, $ToDate("2024-01-01"), "Monthly", 12);
// Result: {
//   totalAmount: 1200,
//   frequency: "Monthly",
//   paymentAmount: 100,
//   paymentCount: 12,
//   schedule: [
//     { paymentNumber: 1, dueDate: 2024-01-01, amount: 100, status: "Pending" },
//     { paymentNumber: 2, dueDate: 2024-02-01, amount: 100, status: "Pending" },
//     ...
//   ]
// }

Late Payment Calculation

Calculate penalties for late payments:

function calculateLateFee(payment, today) {
  // Calculate days late
  daysLate = $DifferenceInDays(payment.dueDate, today);

  if (daysLate <= 0) {
    // Not late
    return {
      "isLate": false,
      "daysLate": 0,
      "lateFee": 0,
      "totalDue": payment.amount
    };
  }

  // Calculate late fee
  if (daysLate <= 15) {
    // Grace period - no fee
    lateFee = 0;
  } else if (daysLate <= 30) {
    // First 30 days: 5% fee
    lateFee = payment.amount * 0.05;
  } else {
    // Over 30 days: 10% fee + $25
    lateFee = payment.amount * 0.1 + 25;
  }

  lateFee = $Round(lateFee * 100) / 100;
  totalDue = payment.amount + lateFee;

  return {
    "isLate": true,
    "daysLate": daysLate,
    "lateFee": lateFee,
    "totalDue": totalDue
  };
}

payment = {
  "id": 1,
  "dueDate": $ToDate("2024-10-01"),
  "amount": 100
};

today = $ToDate("2024-11-15");
lateFeeInfo = calculateLateFee(payment, today);
// Result: {
//   isLate: true,
//   daysLate: 45,
//   lateFee: 35,
//   totalDue: 135
// }

Risk Scoring

Comprehensive Risk Score

Calculate overall risk score from multiple factors:

function calculateRiskScore(applicant) {
  score = 100; // Start with perfect score
  factors = [];

  // Age factor
  age = $DifferenceInYears(applicant.birthDate, $Today());
  if (age < 25) {
    score = score - 15;
    factors.Push("Young driver");
  } else if (age > 70) {
    score = score - 10;
    factors.Push("Senior driver");
  }

  // Driving history
  if (applicant.accidents > 0) {
    penaltyPoints = applicant.accidents * 10;
    score = score - penaltyPoints;
    factors.Push(applicant.accidents.ToString() + " accidents");
  }

  if (applicant.violations > 0) {
    penaltyPoints = applicant.violations * 5;
    score = score - penaltyPoints;
    factors.Push(applicant.violations.ToString() + " violations");
  }

  // Years of experience
  if (applicant.yearsLicensed < 3) {
    score = score - 10;
    factors.Push("Limited experience");
  } else if (applicant.yearsLicensed >= 10) {
    score = score + 5;
    factors.Push("Experienced driver");
  }

  // Credit score
  if (applicant.creditScore < 600) {
    score = score - 20;
    factors.Push("Poor credit");
  } else if (applicant.creditScore >= 750) {
    score = score + 10;
    factors.Push("Excellent credit");
  }

  // Ensure score is between 0 and 100
  if (score < 0) {
    score = 0;
  }
  if (score > 100) {
    score = 100;
  }

  // Determine risk category
  if (score >= 80) {
    category = "Low Risk";
  } else if (score >= 60) {
    category = "Medium Risk";
  } else if (score >= 40) {
    category = "High Risk";
  } else {
    category = "Very High Risk";
  }

  return {
    "score": score,
    "category": category,
    "factors": factors
  };
}

applicant = {
  "name": "John Smith",
  "birthDate": $ToDate("1990-05-15"),
  "accidents": 1,
  "violations": 0,
  "yearsLicensed": 15,
  "creditScore": 720
};

risk = calculateRiskScore(applicant);
// Result: {
//   score: 85,
//   category: "Low Risk",
//   factors: ["1 accidents", "Experienced driver"]
// }

Policy Renewal

Renewal Pricing

Calculate renewal premium with loyalty discounts:

function calculateRenewalPremium(policy, claims) {
  // Start with current premium
  basePremium = policy.currentPremium;

  // Calculate years with company
  yearsWithCompany = $DifferenceInYears(policy.originalDate, $Today());

  // Loyalty discount
  if (yearsWithCompany >= 10) {
    loyaltyDiscount = 0.15; // 15% off
  } else if (yearsWithCompany >= 5) {
    loyaltyDiscount = 0.1; // 10% off
  } else if (yearsWithCompany >= 3) {
    loyaltyDiscount = 0.05; // 5% off
  } else {
    loyaltyDiscount = 0;
  }

  // Claims adjustment (last 3 years)
  cutoffDate = $AddYears($Today(), -3);
  recentClaims = claims.Filter(
    (c) => $DifferenceInDays(cutoffDate, c.date) >= 0
  );

  if (recentClaims.Length == 0) {
    claimsAdjustment = 0.95; // 5% discount
  } else if (recentClaims.Length == 1) {
    claimsAdjustment = 1.1; // 10% increase
  } else {
    claimsAdjustment = 1.25; // 25% increase
  }

  // Market rate adjustment (inflation, etc.)
  marketAdjustment = 1.03; // 3% increase

  // Calculate renewal premium
  adjustedPremium = basePremium * claimsAdjustment * marketAdjustment;
  discountAmount = adjustedPremium * loyaltyDiscount;
  renewalPremium = adjustedPremium - discountAmount;

  return {
    "currentPremium": basePremium,
    "claimsAdjustment": claimsAdjustment,
    "marketAdjustment": marketAdjustment,
    "loyaltyDiscount": loyaltyDiscount,
    "renewalPremium": $Round(renewalPremium * 100) / 100,
    "yearsWithCompany": yearsWithCompany
  };
}

policy = {
  "id": 12345,
  "currentPremium": 1200,
  "originalDate": $ToDate("2015-01-01")
};

claims = []; // No claims

renewal = calculateRenewalPremium(policy, claims);
// Result: {
//   currentPremium: 1200,
//   claimsAdjustment: 0.95,
//   marketAdjustment: 1.03,
//   loyaltyDiscount: 0.15,
//   renewalPremium: 999.27,
//   yearsWithCompany: 9
// }

Portfolio Analysis

Book Roll Summary

Analyze portfolio of policies:

function analyzePortfolio(policies) {
  // Initialize counters
  totalPolicies = policies.Length;
  activePolicies = 0;
  totalPremium = 0;
  averagePremium = 0;

  // Count by status
  statusCounts = { "active": 0, "activePremium": 0, "pending": 0, "lapsed": 0 };
  policies.ForEach((p) => {
    if (p.status == "Active") {
      statusCounts.active = statusCounts.active + 1;
      statusCounts.activePremium = statusCounts.activePremium + p.premium;
    } else if (p.status == "Pending") {
      statusCounts.pending = statusCounts.pending + 1;
    } else if (p.status == "Lapsed") {
      statusCounts.lapsed = statusCounts.lapsed + 1;
    }
  });

  // Group by type
  typeGroups = [];
  policies.ForEach((p) => {
    existing = typeGroups.Find((g) => g.type == p.type);

    if (existing == null) {
      typeGroups.Push({ "type": p.type, "count": 1, "premium": p.premium });
    } else {
      existing.count = existing.count + 1;
      existing.premium = existing.premium + p.premium;
    }
  });

  // Calculate averages
  if (statusCounts.active > 0) {
    averagePremium = statusCounts.activePremium / statusCounts.active;
  }

  // Find largest policy
  largestPolicy = policies.ReduceToNum((max, p) => {
    if (p.premium > max) {
      return p.premium;
    } else {
      return max;
    }
  });

  return {
    "totalPolicies": totalPolicies,
    "activeCount": statusCounts.active,
    "pendingCount": statusCounts.pending,
    "lapsedCount": statusCounts.lapsed,
    "totalActivePremium": statusCounts.activePremium,
    "averagePremium": $Round(averagePremium * 100) / 100,
    "largestPolicy": largestPolicy,
    "byType": typeGroups
  };
}

policies = [
  { "id": 1, "type": "Auto", "premium": 1200, "status": "Active" },
  { "id": 2, "type": "Home", "premium": 1500, "status": "Active" },
  { "id": 3, "type": "Auto", "premium": 850, "status": "Lapsed" },
  { "id": 4, "type": "Life", "premium": 2000, "status": "Active" },
  { "id": 5, "type": "Auto", "premium": 950, "status": "Pending" }
];

analysis = analyzePortfolio(policies);
// Result: {
//   totalPolicies: 5,
//   activeCount: 3,
//   pendingCount: 1,
//   lapsedCount: 1,
//   totalActivePremium: 4700,
//   averagePremium: 1566.67,
//   largestPolicy: 2000,
//   byType: [
//     { type: "Auto", count: 3, premium: 3000 },
//     { type: "Home", count: 1, premium: 1500 },
//     { type: "Life", count: 1, premium: 2000 }
//   ]
// }

Best Practices for Complex Rules

Modular Functions

Break complex rules into smaller, testable functions:

// Good: Small, focused functions
function calculateAgeFactor(age) {
  if (age < 25) {
    return 1.5;
  } else if (age < 65) {
    return 1.0;
  } else {
    return 1.3;
  }
}

function calculateRecordFactor(accidents) {
  if (accidents > 2) {
    return 1.8;
  } else if (accidents > 0) {
    return 1.3;
  } else {
    return 0.9;
  }
}

function calculatePremium(base, age, accidents) {
  ageFactor = calculateAgeFactor(age);
  recordFactor = calculateRecordFactor(accidents);
  return base * ageFactor * recordFactor;
}

Validation First

Validate inputs before processing:

function processApplication(application) {
  // Validate first
  validation = validateApplication(application);

  if (!validation.isValid) {
    return {
      "success": false,
      "errors": validation.errors
    };
  }

  // Then process
  result = calculatePremium(application);

  return {
    "success": true,
    "premium": result
  };
}

Return Detailed Results

Provide context with calculations:

// Good: Return breakdown
return {
  "totalPremium": premium,
  "basePremium": base,
  "ageFactor": ageFactor,
  "recordFactor": recordFactor,
  "calculation": "base * ageFactor * recordFactor"
};

// Less helpful: Return only final value
// return premium;

Next Steps

See Also