Fix short-rate calculation, add styling, add error messages, include disclaimer

This commit is contained in:
Hao Tran 2025-01-11 08:38:12 -05:00
parent 3dadf20dbf
commit 26ce9b5c8a
6 changed files with 879 additions and 292 deletions

561
assets/calculator.js Normal file
View File

@ -0,0 +1,561 @@
const shortRate = {
1: 0.05,
2: 0.06,
3: 0.07,
4: 0.07,
5: 0.08,
6: 0.08,
7: 0.09,
8: 0.09,
9: 0.10,
10: 0.10,
11: 0.11,
12: 0.11,
13: 0.12,
14: 0.12,
15: 0.13,
16: 0.13,
17: 0.14,
18: 0.14,
19: 0.15,
20: 0.15,
21: 0.16,
22: 0.16,
23: 0.17,
24: 0.17,
25: 0.17,
26: 0.18,
27: 0.18,
28: 0.18,
29: 0.18,
30: 0.19,
31: 0.19,
32: 0.19,
33: 0.20,
34: 0.20,
35: 0.20,
36: 0.20,
37: 0.21,
38: 0.21,
39: 0.21,
40: 0.21,
41: 0.22,
42: 0.22,
43: 0.22,
44: 0.77,
45: 0.77,
46: 0.77,
47: 0.77,
48: 0.24,
49: 0.24,
50: 0.24,
51: 0.24,
52: 0.25,
53: 0.25,
54: 0.25,
55: 0.26,
56: 0.26,
57: 0.26,
58: 0.26,
59: 0.27,
60: 0.27,
61: 0.27,
62: 0.27,
63: 0.28,
64: 0.28,
65: 0.28,
66: 0.29,
67: 0.29,
68: 0.29,
69: 0.29,
70: 0.30,
71: 0.30,
72: 0.30,
73: 0.30,
74: 0.31,
75: 0.31,
76: 0.31,
77: 0.32,
78: 0.32,
79: 0.32,
80: 0.32,
81: 0.33,
82: 0.33,
83: 0.33,
84: 0.34,
85: 0.34,
86: 0.34,
87: 0.34,
88: 0.35,
89: 0.35,
90: 0.35,
91: 0.35,
92: 0.36,
93: 0.36,
94: 0.36,
95: 0.37,
96: 0.37,
97: 0.37,
98: 0.37,
99: 0.38,
100: 0.38,
101: 0.38,
102: 0.38,
103: 0.39,
104: 0.39,
105: 0.39,
106: 0.40,
107: 0.40,
108: 0.40,
109: 0.40,
110: 0.41,
111: 0.41,
112: 0.41,
113: 0.41,
114: 0.42,
115: 0.42,
116: 0.42,
117: 0.43,
118: 0.43,
119: 0.43,
120: 0.43,
121: 0.44,
122: 0.44,
123: 0.44,
124: 0.44,
125: 0.45,
126: 0.45,
127: 0.45,
128: 0.46,
129: 0.46,
130: 0.46,
131: 0.46,
132: 0.47,
133: 0.47,
134: 0.47,
135: 0.47,
136: 0.48,
137: 0.48,
138: 0.48,
139: 0.49,
140: 0.49,
141: 0.49,
142: 0.49,
143: 0.50,
144: 0.50,
145: 0.50,
146: 0.50,
147: 0.51,
148: 0.51,
149: 0.51,
150: 0.52,
151: 0.52,
152: 0.52,
153: 0.52,
154: 0.53,
155: 0.53,
156: 0.53,
157: 0.53,
158: 0.54,
159: 0.54,
160: 0.54,
161: 0.55,
162: 0.55,
163: 0.55,
164: 0.55,
165: 0.56,
166: 0.56,
167: 0.56,
168: 0.57,
169: 0.57,
170: 0.57,
171: 0.57,
172: 0.58,
173: 0.58,
174: 0.58,
175: 0.58,
176: 0.59,
177: 0.59,
178: 0.59,
179: 0.60,
180: 0.60,
181: 0.60,
182: 0.60,
183: 0.61,
184: 0.61,
185: 0.61,
186: 0.61,
187: 0.61,
188: 0.62,
189: 0.62,
190: 0.62,
191: 0.62,
192: 0.63,
193: 0.63,
194: 0.63,
195: 0.63,
196: 0.63,
197: 0.64,
198: 0.64,
199: 0.64,
200: 0.64,
201: 0.65,
202: 0.65,
203: 0.65,
204: 0.65,
205: 0.65,
206: 0.66,
207: 0.66,
208: 0.66,
209: 0.66,
210: 0.67,
211: 0.67,
212: 0.67,
213: 0.67,
214: 0.67,
215: 0.68,
216: 0.68,
217: 0.68,
218: 0.68,
219: 0.69,
220: 0.69,
221: 0.69,
222: 0.69,
223: 0.69,
224: 0.70,
225: 0.70,
226: 0.70,
227: 0.70,
228: 0.70,
229: 0.71,
230: 0.71,
231: 0.71,
232: 0.71,
233: 0.72,
234: 0.72,
235: 0.72,
236: 0.72,
237: 0.72,
238: 0.73,
239: 0.73,
240: 0.73,
241: 0.73,
242: 0.74,
243: 0.74,
244: 0.74,
245: 0.74,
246: 0.74,
247: 0.75,
248: 0.75,
249: 0.75,
250: 0.75,
251: 0.76,
252: 0.76,
253: 0.76,
254: 0.76,
255: 0.76,
256: 0.77,
257: 0.77,
258: 0.77,
259: 0.77,
260: 0.77,
261: 0.78,
262: 0.78,
263: 0.78,
264: 0.78,
265: 0.79,
266: 0.79,
267: 0.79,
268: 0.79,
269: 0.79,
270: 0.80,
271: 0.80,
272: 0.80,
273: 0.80,
274: 0.81,
275: 0.81,
276: 0.81,
277: 0.81,
278: 0.81,
279: 0.82,
280: 0.82,
281: 0.82,
282: 0.82,
283: 0.83,
284: 0.83,
285: 0.83,
286: 0.83,
287: 0.83,
288: 0.84,
289: 0.84,
290: 0.84,
291: 0.84,
292: 0.85,
293: 0.85,
294: 0.85,
295: 0.85,
296: 0.85,
297: 0.86,
298: 0.86,
299: 0.86,
300: 0.86,
301: 0.86,
302: 0.87,
303: 0.87,
304: 0.87,
305: 0.87,
306: 0.88,
307: 0.88,
308: 0.88,
309: 0.88,
310: 0.88,
311: 0.89,
312: 0.89,
313: 0.89,
314: 0.89,
315: 0.90,
316: 0.90,
317: 0.90,
318: 0.90,
319: 0.90,
320: 0.91,
321: 0.91,
322: 0.91,
323: 0.91,
324: 0.92,
325: 0.92,
326: 0.92,
327: 0.92,
328: 0.92,
329: 0.93,
330: 0.93,
331: 0.93,
332: 0.93,
333: 0.94,
334: 0.94,
335: 0.94,
336: 0.94,
337: 0.94,
338: 0.95,
339: 0.95,
340: 0.95,
341: 0.95,
342: 0.95,
343: 0.96,
344: 0.96,
345: 0.96,
346: 0.96,
347: 0.97,
348: 0.97,
349: 0.97,
350: 0.97,
351: 0.97,
352: 0.98,
353: 0.98,
354: 0.98,
355: 0.98,
356: 0.99,
357: 0.99,
358: 0.99,
360: 0.99,
359: 0.99,
361: 1.00,
362: 1.00,
363: 1.00,
364: 1.00,
365: 1.00,
366: 1.00,
};
function calculator() {
// pull from input fields
var effectiveDate = document.getElementById("effectiveDate").value;
var expiryDate = document.getElementById("expiryDate").value;
var cancelDate = document.getElementById("cancelDate").value;
var totalPremium = document.getElementById("totalPremium").value;
var breakDO = document.getElementById("breakDO").value || 0;
var breakEO = document.getElementById("breakEO").value || 0;
var breakCyber = document.getElementById("breakCyber").value || 0;
var breakLEI = document.getElementById("breakLEI").value || 0;
var breakCGL = document.getElementById("breakCGL").value || 0;
var breakProperty = document.getElementById("breakProperty").value || 0;
var breakEB = document.getElementById("breakEB").value || 0;
// calculate dates in policy term
var policyDays = new Date(expiryDate).getTime() - new Date(effectiveDate).getTime();
var termDays = policyDays / (1000 * 60 * 60 * 24);
// calculate dates involved with cancellation
var cancelDays = new Date(cancelDate).getTime() - new Date(effectiveDate).getTime();
var forceDays = cancelDays / (1000 * 60 * 60 * 24);
var remainingDays = termDays - forceDays;
// calculate pro rata
if(document.getElementById('calcProRata').checked) {
var earnedFactor = Math.round(((forceDays / termDays) + Number.EPSILON)*1000)/1000;
var unearnedFactor = Math.round(((remainingDays / termDays) + Number.EPSILON)*1000)/1000;
document.getElementById("earnedFactor").className = 'blue';
document.getElementById("unearnedFactor").className = 'blue';
// calculate short rate
}else if(document.getElementById('calcShortRate').checked) {
const searchDate = forceDays;
const shortRateFactor = shortRate[searchDate];
var earnedFactor = shortRateFactor;
var unearnedFactor = Math.round(((1 - shortRateFactor) + Number.EPSILON)*1000)/1000;
document.getElementById("earnedFactor").className = 'blue';
document.getElementById("unearnedFactor").className = 'blue';
}else {
var earnedFactor = 'ERROR'
var unearnedFactor = 'ERROR'
document.getElementById("earnedFactor").className = 'red';
document.getElementById("unearnedFactor").className = 'red';
}
// calculate premiums
var earnedPremium = Math.round(((totalPremium * earnedFactor) + Number.EPSILON)*100)/100;
var unearnedPremium = Math.round(((totalPremium * unearnedFactor) + Number.EPSILON)*100)/100;
document.getElementById("earnedPremium").className = 'green';
document.getElementById("unearnedPremium").className = 'orange';
// calculate breakdown
var calcBreak = +breakDO + +breakEO + +breakCyber + +breakLEI + +breakCGL + +breakProperty + +breakEB;
// check if breakdown matches total premium
var calcMatch = calcBreak-totalPremium;
if(totalPremium<calcBreak && totalPremium > 1) {
var checkMatch = "❌ Total breakdown does not match total premium. It is too high by " + calcMatch + ".";
document.getElementById("checkMatch").className = 'red';
var calculateBreakdown = false;
}else if(totalPremium>calcBreak && totalPremium > 1) {
var checkMatch = "❌ Total breakdown does not match total premium. It is too low by " + calcMatch + ".";
document.getElementById("checkMatch").className = 'red';
var calculateBreakdown = false;
}else if(totalPremium > 1) {
var checkMatch = "✅ Total breakdown matches total premium!";
document.getElementById("checkMatch").className = 'green';
var calculateBreakdown = true;
}else {
var checkMatch = "⚠️ More information is required to provide premium breakdown.";
document.getElementById("checkMatch").className = 'orange';
var calculateBreakdown = false;
}
// calculate breakdowns
if(calculateBreakdown == true) {
var percentDO = Math.round((((breakDO / totalPremium) * 100) + Number.EPSILON)*100)/100;
var earnedDO = Math.round(((breakDO * earnedFactor) + Number.EPSILON)*100)/100;
var unearnedDO = Math.round(((breakDO * unearnedFactor) + Number.EPSILON)*100)/100;
var percentEO = Math.round((((breakEO / totalPremium) * 100) + Number.EPSILON)*100)/100;
var earnedEO = Math.round(((breakEO * earnedFactor) + Number.EPSILON)*100)/100;
var unearnedEO = Math.round(((breakEO * unearnedFactor) + Number.EPSILON)*100)/100;
var percentCyber = Math.round((((breakCyber / totalPremium) * 100) + Number.EPSILON)*100)/100;
var earnedCyber = Math.round(((breakCyber * earnedFactor) + Number.EPSILON)*100)/100;
var unearnedCyber = Math.round(((breakCyber * unearnedFactor) + Number.EPSILON)*100)/100;
var percentLEI = Math.round((((breakLEI / totalPremium) * 100) + Number.EPSILON)*100)/100;
var earnedLEI = Math.round(((breakLEI * earnedFactor) + Number.EPSILON)*100)/100;
var unearnedLEI = Math.round(((breakLEI * unearnedFactor) + Number.EPSILON)*100)/100;
var percentCGL = Math.round((((breakCGL / totalPremium) * 100) + Number.EPSILON)*100)/100;
var earnedCGL = Math.round(((breakCGL * earnedFactor) + Number.EPSILON)*100)/100;
var unearnedCGL = Math.round(((breakCGL * unearnedFactor) + Number.EPSILON)*100)/100;
var percentProperty = Math.round((((breakProperty / totalPremium) * 100) + Number.EPSILON)*100)/100;
var earnedProperty = Math.round(((breakProperty * earnedFactor) + Number.EPSILON)*100)/100;
var unearnedProperty = Math.round(((breakProperty * unearnedFactor) + Number.EPSILON)*100)/100;
var percentEB = Math.round((((breakEB / totalPremium) * 100) + Number.EPSILON)*100)/100;
var earnedEB = Math.round(((breakEB * earnedFactor) + Number.EPSILON)*100)/100;
var unearnedEB = Math.round(((breakEB * unearnedFactor) + Number.EPSILON)*100)/100;
document.getElementById("percentDO").className = 'blue';
document.getElementById("earnedDO").className = 'green';
document.getElementById("unearnedDO").className = 'orange';
document.getElementById("percentEO").className = 'blue';
document.getElementById("earnedEO").className = 'green';
document.getElementById("unearnedEO").className = 'orange';
document.getElementById("percentCyber").className = 'blue';
document.getElementById("earnedCyber").className = 'green';
document.getElementById("unearnedCyber").className = 'orange';
document.getElementById("percentLEI").className = 'blue';
document.getElementById("earnedLEI").className = 'green';
document.getElementById("unearnedLEI").className = 'orange';
document.getElementById("percentCGL").className = 'blue';
document.getElementById("earnedCGL").className = 'green';
document.getElementById("unearnedCGL").className = 'orange';
document.getElementById("percentProperty").className = 'blue';
document.getElementById("earnedProperty").className = 'green';
document.getElementById("unearnedProperty").className = 'orange';
document.getElementById("percentEB").className = 'blue';
document.getElementById("earnedEB").className = 'green';
document.getElementById("unearnedEB").className = 'orange';
}else {
var percentDO = 'ERROR'
var earnedDO = 'ERROR'
var unearnedDO = 'ERROR'
var percentEO = 'ERROR'
var earnedEO = 'ERROR'
var unearnedEO = 'ERROR'
var percentCyber = 'ERROR'
var earnedCyber = 'ERROR'
var unearnedCyber = 'ERROR'
var percentLEI = 'ERROR'
var earnedLEI = 'ERROR'
var unearnedLEI = 'ERROR'
var percentCGL = 'ERROR'
var earnedCGL = 'ERROR'
var unearnedCGL = 'ERROR'
var percentProperty = 'ERROR'
var earnedProperty = 'ERROR'
var unearnedProperty = 'ERROR'
var percentEB = 'ERROR'
var earnedEB = 'ERROR'
var unearnedEB = 'ERROR'
document.getElementById("percentDO").className = 'red';
document.getElementById("earnedDO").className = 'red';
document.getElementById("unearnedDO").className = 'red';
document.getElementById("percentEO").className = 'red';
document.getElementById("earnedEO").className = 'red';
document.getElementById("unearnedEO").className = 'red';
document.getElementById("percentCyber").className = 'red';
document.getElementById("earnedCyber").className = 'red';
document.getElementById("unearnedCyber").className = 'red';
document.getElementById("percentLEI").className = 'red';
document.getElementById("earnedLEI").className = 'red';
document.getElementById("unearnedLEI").className = 'red';
document.getElementById("percentCGL").className = 'red';
document.getElementById("earnedCGL").className = 'red';
document.getElementById("unearnedCGL").className = 'red';
document.getElementById("percentProperty").className = 'red';
document.getElementById("earnedProperty").className = 'red';
document.getElementById("unearnedProperty").className = 'red';
document.getElementById("percentEB").className = 'red';
document.getElementById("earnedEB").className = 'red';
document.getElementById("unearnedEB").className = 'red';
}
// display policy terms
document.getElementById("termDays").innerHTML = termDays;
document.getElementById("forceDays").innerHTML = forceDays;
document.getElementById("remainingDays").innerHTML = remainingDays;
document.getElementById("earnedFactor").innerHTML = earnedFactor;
document.getElementById("unearnedFactor").innerHTML = unearnedFactor;
document.getElementById("earnedPremium").innerHTML = earnedPremium;
document.getElementById("unearnedPremium").innerHTML = unearnedPremium;
// display breakdown
document.getElementById("checkMatch").innerHTML = checkMatch;
document.getElementById("percentDO").innerHTML = percentDO;
document.getElementById("earnedDO").innerHTML = earnedDO;
document.getElementById("unearnedDO").innerHTML = unearnedDO;
document.getElementById("percentEO").innerHTML = percentEO;
document.getElementById("earnedEO").innerHTML = earnedEO;
document.getElementById("unearnedEO").innerHTML = unearnedEO;
document.getElementById("percentCyber").innerHTML = percentCyber;
document.getElementById("earnedCyber").innerHTML = earnedCyber;
document.getElementById("unearnedCyber").innerHTML = unearnedCyber;
document.getElementById("percentLEI").innerHTML = percentLEI;
document.getElementById("earnedLEI").innerHTML = earnedLEI;
document.getElementById("unearnedLEI").innerHTML = unearnedLEI;
document.getElementById("percentCGL").innerHTML = percentCGL;
document.getElementById("earnedCGL").innerHTML = earnedCGL;
document.getElementById("unearnedCGL").innerHTML = unearnedCGL;
document.getElementById("percentProperty").innerHTML = percentProperty;
document.getElementById("earnedProperty").innerHTML = earnedProperty;
document.getElementById("unearnedProperty").innerHTML = unearnedProperty;
document.getElementById("percentEB").innerHTML = percentEB;
document.getElementById("earnedEB").innerHTML = earnedEB;
document.getElementById("unearnedEB").innerHTML = unearnedEB;
}

BIN
assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

190
assets/style.css Normal file
View File

@ -0,0 +1,190 @@
html, body, div, span, applet, object, iframe,
:root {
--width: 900px;
--font-family: Verdana, sans-serif;
--font-scale: 1em;
--bg-100: #100F0F;
--bg-200: #1C1B1A;
--ui-100: #282726;
--ui-200: #343331;
--ui-300: #403E3C;
--text-100: #CECDC3;
--text-200: #878580;
--text-300: #575653;
--blue-400: #4385BE;
--cyan-400: #3AA99F;
--green-400: #879A39;
--orange-400: #DA702C;
--purple-400: #8B7EC8;
--red-400: #D14D41;
--yellow-400: #D0A215;
}
@media (prefers-color-scheme: dark) {
:root {
}
}
body {
margin: 0 auto;
padding: 0 16px;
max-width: var(--width);
background-color: var(--bg-100);
font-family: var(--font-family);
font-size: var(--font-scale);
color: var(--text-100);
}
.blue { color: var(--blue-400); }
.cyan { color: var(--cyan-400); }
.green { color: var(--green-400); }
.orange { color: var(--orange-400); }
.purple { color: var(--purple-400); }
.red { color: var(--red-400); }
h1, h2, h3 {
margin: 0;
padding: 0;
}
h2 {
margin: 1rem 0;
}
a,
a:visited {
color: var(--text-100);
text-decoration: underline;
text-underline-offset: 3px;
text-decoration-thickness: 1px;
text-decoration-style: solid;
text-decoration-color: var(--cyan-400);
}
a:hover {
color: var(--cyan-400);
text-decoration: underline;
text-decoration-style: solid;
text-decoration-color: var(--cyan-400);
}
ul {
margin: 0;
padding: 0 0 0 1rem;
}
li {
list-style-type: square;
}
form {
margin: 0;
padding: 0;
}
button {
margin-top: 2rem;
padding: 10px;
width: 100%;
border: 1px solid var(--ui-100);
background-color: var(--bg-200);
color: var(--text-100);
}
button:hover{
border: 1px solid var(--ui-300);
}
input {
border: 1px solid var(--ui-100);
background-color: var(--bg-200);
color: var(--text-100);
}
input:hover {
border-color: var(--ui-300);
}
input:focus {
outline: none;
border-color: var(--cyan-400);
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
blockquote {
margin: 1rem 0;
padding: 16px;
border-top: 1px dotted var(--yellow-400);
border-right: 1px dotted var(--yellow-400);
border-bottom: 1px dotted var(--yellow-400);
border-left: 8px solid var(--yellow-400);
background-color: var(--bg-200);
}
table {
width: 100%;
border-collapse: collapse;
border: 1px solid var(--ui-100);
background-color: var(--bg-200);
}
tr {
border: 1px solid var(--ui-100);
}
th {
padding: 4px 16px;
border-right: 1px solid var(--ui-100);
background-color: var(--ui-200);
}
td {
padding: 4px 16px;
border-right: 1px solid var(--ui-100);
}
tr td:nth-child(3) {
text-align: center;
}
tr td:nth-child(4),
tr td:nth-child(5) {
text-align: right;
}
header {
margin-top: 4rem;
}
main {
margin: 4rem 0;
}
.flex-container {
display: flex;
margin: 0;
padding: 0;
flex-direction: row;
gap: 2rem 1rem;
flex-wrap: wrap;
align-items: flex-start;
justify-content: center;
}
.flex-1 {
order: 1;
flex-grow: 1;
}
.flex-2{
order: 2;
flex-grow: 1
}
.flex-3 {
order: 3;
flex-grow: 2;
}
.flex-4 {
order: 4;
flex-grow: 2;
}
footer {
margin-bottom: 4rem;
padding-top: 2rem;
border-top: 1px dotted var(--ui-100);
font-size: .875em;
color: var(--text-200);
}

View File

@ -3,122 +3,148 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name=""viewport" content=""width=device-width, initial-scale="1.0"> <meta name=""viewport" content=""width=device-width, initial-scale="1.0">
<title>Calculator</title> <title>Calculator</title>
<link rel="stylesheet" href="style.css">
<meta name="description" content="Insurance premium calculator for pro-rata and short-rate calculations.?">
<link rel="stylesheet" href="assets/style.css">
<link rel="icon" type="image/x-icon" href="assets/favicon.ico">
</head> </head>
<body> <body>
<header> <header>
<h1>Calculator</h1> <h1>Premium Calculator</h1>
</header> </header>
<main> <main>
<form action="/action_page.php"> <form action="/action_page.php">
<div class="flex-container"> <div class="flex-container">
<div class="flex-1"> <div class="flex-1">
<h2>Policy Details</h2> <h2>Policy Details</h2>
<label for="effectiveDate">Effective date:</label> <label for="effectiveDate">Effective date:</label>
<input type="date" id="effectiveDate" name="effectiveDate" onblur="calculator()" min="0" oninput="validity.valid||(value='')"> <input type="date" id="effectiveDate" name="effectiveDate" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br> <br>
<label for="expiryDate">Expiry date:</label> <label for="expiryDate">Expiry date:</label>
<input type="date" id="expiryDate" name="expiryDate" onblur="calculator()" min="0" oninput="validity.valid||(value='')"> <input type="date" id="expiryDate" name="expiryDate" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br> <br>
Days in policy term: <span id="termDays">-</span> Days in policy term: <span id="termDays">-</span>
<br><br> <br><br>
<label for="cancelDate">Cancellation date</label> <label for="cancelDate">Cancellation date:</label>
<input type="date" id="cancelDate" name="cancelDate" onblur="calculator()" min="0" oninput="validity.valid||(value='')"> <input type="date" id="cancelDate" name="cancelDate" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br> <br>
Days in force: <span id="forceDays">-</span> Days in force: <span id="forceDays">-</span>
<br> <br>
Days remaining: <span id="remainingDays">-</span> Days remaining: <span id="remainingDays">-</span>
<br><br> <br><br>
Factor method: <label for="totalPremium">Total premium:</label>
<input type="radio" name="calcMethod" id="calcProRata" value="Pro Rata" onclick="calculator();" /> Pro Rata <input type="number" id="totalPremium" name="totalPremium" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<input type="radio" name="calcMethod" id="calcShortRate" value="Short Rate" onclick="calculator();"/> Short Rate
<br><br>
Earned factor: <span id="earnedFactor">-</span>
<br>
Unearned factor: <span id="unearnedFactor">-</span>
<br><br>
<label for="totalPremium">Total premium:</label>
<input type="number" id="totalPremium" name="totalPremium" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedPremium">-</span>
<br>
Return premium: <span id="unearnedPremium">-</span>
</div>
<div class="flex-2">
<h2>Breakdown</h2>
<p><span id="checkMatch">Total breakdown: N/A</span></p>
<label for="breakDO">D&O:</label>
<input type="number" id="breakDO" name="breakDO" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedDO">-</span>
<br>
Return premium: <span id="unearnedDO">-</span>
<br><br>
<label for="breakEO">E&O:</label>
<input type="number" id="breakEO" name="breakEO" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedEO">-</span>
<br>
Return premium: <span id="unearnedEO">-</span>
<br><br>
<label for="breakCyber">Cyber:</label>
<input type="number" id="breakCyber" name="breakCyber" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedCyber">-</span>
<br>
Return premium: <span id="unearnedCyber">-</span>
<br><br>
<label for="breakLEI">Legal Expense:</label>
<input type="number" id="breakLEI" name="breakLEI" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedLEI">-</span>
<br>
Return premium: <span id="unearnedLEI">-</span>
<br><br>
<label for="breakCGL">CGL:</label>
<input type="number" id="breakCGL" name="breakCGL" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedCGL">-</span>
<br>
Return premium: <span id="unearnedCGL">-</span>
<br><br>
<label for="breakProperty">Property:</label>
<input type="number" id="breakProperty" name="breakProperty" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedProperty">-</span>
<br>
Return premium: <span id="unearnedProperty">-</span>
<br><br>
<label for="breakEB">Equipment Breakdown::</label>
<input type="number" id="breakEB" name="breakEB" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedEB">-</span>
<br>
Return premium: <span id="unearnedEB">-</span>
<br><br>
</div>
</div> </div>
<div class="flex-2">
<h2>Factor Method</h2>
Factor method:
<input type="radio" name="calcMethod" id="calcProRata" value="Pro Rata" checked="checked" onclick="calculator();" /> Pro Rata
<input type="radio" name="calcMethod" id="calcShortRate" value="Short Rate" onclick="calculator();"/> Short Rate
<br><br>
<table>
<tr>
<th></th>
<th>Earned</th>
<th>Unearned</th>
</tr>
<tr>
<td>Factor</td>
<td><span id="earnedFactor">-</span></td>
<td><span id="unearnedFactor">-</span></td>
</tr>
<tr>
<td>Premium</td>
<td><span id="earnedPremium">-</span></td>
<td><span id="unearnedPremium">-</span></td>
</tr>
</table>
</div>
<div class="flex-3">
<h2>Premium Breakdown</h2>
<blockquote><span id="checkMatch">Total breakdown: N/A</span></blockquote>
<table>
<tr>
<th>Coverage</th>
<th>Premium</th>
<th>% of Total</th>
<th>Earned Premium</th>
<th>Unearned Premium </th>
</tr>
<tr>
<td><label for="breakDO">D&O</label></td>
<td><input type="number" id="breakDO" name="breakDO" onblur="calculator()" min="0" oninput="validity.valid||(value='')"></td>
<td><span id="percentDO">-</span></td>
<td><span id="earnedDO">-</span></td>
<td><span id="unearnedDO">-</span></td>
</tr>
<tr>
<td><label for="breakEO">E&O</label></td>
<td><input type="number" id="breakEO" name="breakEO" onblur="calculator()" min="0" oninput="validity.valid||(value='')"></td>
<td><span id="percentEO">-</span></td>
<td><span id="earnedEO">-</span></td>
<td><span id="unearnedEO">-</span></td>
</tr>
<tr>
<td><label for="breakCyber">Cyber</label></td>
<td><input type="number" id="breakCyber" name="breakCyber" onblur="calculator()" min="0" oninput="validity.valid||(value='')"></td>
<td><span id="percentCyber">-</span></td>
<td><span id="earnedCyber">-</span></td>
<td><span id="unearnedCyber">-</span></td>
</tr>
<tr>
<td><label for="breakLEI">Legal Expense</label></td>
<td><input type="number" id="breakLEI" name="breakLEI" onblur="calculator()" min="0" oninput="validity.valid||(value='')"></td>
<td><span id="percentLEI">-</span></td>
<td><span id="earnedLEI">-</span></td>
<td><span id="unearnedLEI">-</span></td>
</tr>
<tr>
<td><label for="breakCGL">CGL</label></td>
<td><input type="number" id="breakCGL" name="breakCGL" onblur="calculator()" min="0" oninput="validity.valid||(value='')"></td>
<td><span id="percentCGL">-</span></td>
<td><span id="earnedCGL">-</span></td>
<td><span id="unearnedCGL">-</span></td>
</tr>
<tr>
<td><label for="breakProperty">Property</label></td>
<td><input type="number" id="breakProperty" name="breakProperty" onblur="calculator()" min="0" oninput="validity.valid||(value='')"></td>
<td><span id="percentProperty">-</span></td>
<td><span id="earnedProperty">-</span></td>
<td><span id="unearnedProperty">-</span></td>
</tr>
<tr>
<td><label for="breakEB">Equipment Breakdown</label></td>
<td><input type="number" id="breakEB" name="breakEB" onblur="calculator()" min="0" oninput="validity.valid||(value='')"></td>
<td><span id="percentEB">-</span></td>
<td><span id="earnedEB">-</span></td>
<td><span id="unearnedEB">-</span></td>
</tr>
</table>
</form> </form>
<button onclick="calculator()">Calculate</button> <div class="flex-4">
<button onclick="calculator()">Calculate</button>
</div>
</div>
</main> </main>
<footer> <footer>
<p>Created by <a href="https://haothitran.com">Hao Tran</a>.</p> <ul>
<p>Note: Calculations are rounded to the nearest hundredth decimal. As such, there may sometimes be issues of rounding error.</p> <li>Created by <a href="https://haothitran.com">Hao Tran</a>.</li>
<li>Calculations are rounded to the nearest hundredth decimal. As such, there may sometimes be issues of rounding error.</li>
<li>Although effort was made to insure the accuracy of this calculator, there is no guarantee on the accuracy of the calculations and results. This calculator is intended for personal and informational use only and does not constitute advice. </li>
</ul>
</footer> </footer>
<script src="assets/calculator.js"></script>
</body> </body>
<script src="scripts/calculator.js"></script>
</html> </html>

View File

@ -1,114 +0,0 @@
function calculator() {
// pull from input fields
var effectiveDate = document.getElementById("effectiveDate").value;
var expiryDate = document.getElementById("expiryDate").value;
var cancelDate = document.getElementById("cancelDate").value;
var totalPremium = document.getElementById("totalPremium").value;
var breakDO = document.getElementById("breakDO").value || 0;
var breakEO = document.getElementById("breakEO").value || 0;
var breakCyber = document.getElementById("breakCyber").value || 0;
var breakLEI = document.getElementById("breakLEI").value || 0;
var breakCGL = document.getElementById("breakCGL").value || 0;
var breakProperty = document.getElementById("breakProperty").value || 0;
var breakEB = document.getElementById("breakEB").value || 0;
// calculate dates in policy term
var policyDays = new Date(expiryDate).getTime() - new Date(effectiveDate).getTime();
var termDays = policyDays / (1000 * 60 * 60 * 24);
// calculate dates involved with cancellation
var cancelDays = new Date(cancelDate).getTime() - new Date(effectiveDate).getTime();
var forceDays = cancelDays / (1000 * 60 * 60 * 24);
var remainingDays = termDays - forceDays;
// calculate pro rata
if(document.getElementById('calcProRata').checked) {
var earnedFactor = Math.round(((forceDays / termDays) + Number.EPSILON)*100)/100;
var unearnedFactor = Math.round(((remainingDays / termDays) + Number.EPSILON)*100)/100;
// calculate short rate
}else if(document.getElementById('calcShortRate').checked) {
var earnedFactor = Math.round(((1 - ((remainingDays / termDays ) * 0.9)) + Number.EPSILON)*100)/100;
var unearnedFactor = Math.round((((remainingDays / termDays) * 0.9) + Number.EPSILON)*100)/100;
}
// calculate premiums
var earnedPremium = Math.round(((totalPremium * earnedFactor) + Number.EPSILON)*100)/100;
var unearnedPremium = Math.round(((totalPremium * unearnedFactor) + Number.EPSILON)*100)/100;
// calculate breakdown
var calcBreak = +breakDO + +breakEO + +breakCyber + +breakLEI + +breakCGL + +breakProperty + +breakEB;
// check if breakdown matches total premium
var calcMatch = calcBreak-totalPremium;
if(totalPremium<calcBreak) {
var checkMatch = "Total breakdown is too high by " + calcMatch + ".";
document.getElementById("checkMatch").className = 'red';
var calculateBreakdown = false;
}else if(totalPremium>calcBreak) {
var checkMatch = "Total breakdown is too low by " + calcMatch + ".";
document.getElementById("checkMatch").className = 'red';
var calculateBreakdown = false;
}else {
var checkMatch = "Total breakdown matches total premium! Success!";
document.getElementById("checkMatch").className = 'green';
var calculateBreakdown = true;
}
// calculate breakdowns
if(calculateBreakdown == true) {
var earnedDO = Math.round(((breakDO * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedDO = Math.round(((breakDO * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedEO = Math.round(((breakEO * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedEO = Math.round(((breakEO * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedCyber = Math.round(((breakCyber * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedCyber = Math.round(((breakCyber * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedLEI = Math.round(((breakLEI * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedLEI = Math.round(((breakLEI * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedCGL = Math.round(((breakCGL * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedCGL = Math.round(((breakCGL * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedProperty = Math.round(((breakProperty * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedProperty = Math.round(((breakProperty * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedEB = Math.round(((breakEB * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedEB = Math.round(((breakEB * unearnedFactor) + Number.EPSILON)*1000)/1000;
}else {
var earnedDO = 'ERROR'
var unearnedDO = 'ERROR'
var earnedEO = 'ERROR'
var unearnedEO = 'ERROR'
var earnedCyber = 'ERROR'
var unearnedCyber = 'ERROR'
var earnedLEI = 'ERROR'
var unearnedLEI = 'ERROR'
var earnedCGL = 'ERROR'
var unearnedCGL = 'ERROR'
var earnedProperty = 'ERROR'
var unearnedProperty = 'ERROR'
var earnedEB = 'ERROR'
var unearnedEB = 'ERROR'
}
// display policy terms
document.getElementById("termDays").innerHTML = termDays;
document.getElementById("forceDays").innerHTML = forceDays;
document.getElementById("remainingDays").innerHTML = remainingDays;
document.getElementById("earnedFactor").innerHTML = earnedFactor;
document.getElementById("unearnedFactor").innerHTML = unearnedFactor;
document.getElementById("earnedPremium").innerHTML = earnedPremium;
document.getElementById("unearnedPremium").innerHTML = unearnedPremium;
// display breakdown
document.getElementById("checkMatch").innerHTML = checkMatch;
document.getElementById("earnedDO").innerHTML = earnedDO;
document.getElementById("unearnedDO").innerHTML = unearnedDO;
document.getElementById("earnedEO").innerHTML = earnedEO;
document.getElementById("unearnedEO").innerHTML = unearnedEO;
document.getElementById("earnedCyber").innerHTML = earnedCyber;
document.getElementById("unearnedCyber").innerHTML = unearnedCyber;
document.getElementById("earnedLEI").innerHTML = earnedLEI;
document.getElementById("unearnedLEI").innerHTML = unearnedLEI;
document.getElementById("earnedCGL").innerHTML = earnedCGL;
document.getElementById("unearnedCGL").innerHTML = unearnedCGL;
document.getElementById("earnedProperty").innerHTML = earnedProperty;
document.getElementById("unearnedProperty").innerHTML = unearnedProperty;
document.getElementById("earnedEB").innerHTML = earnedEB;
document.getElementById("unearnedEB").innerHTML = unearnedEB;
}
doSomething

View File

@ -1,76 +0,0 @@
:root {
--width: 900px;
--font-family: Verdana, sans-serif;
--font-scale: 1em;
--bg-100: #100F0F;
--bg-200: #1C1B1A;
--ui-100: #292726;
--ui-200: #343331;
--ui-300: #403E3C;
--text-100: #CECDC3;
--cyan-400: #3AA99F;
--green-400: #879A39;
--red-400: #D14D41;
}
@media (prefers-color-scheme: dark) {
:root {
}
}
body {
margin: 0 auto;
padding: 0 16px;
max-width: var(--width);
background-color: var(--bg-100);
font-family: var(--font-family);
font-size: var(--font-scale);
color: var(--text-100);
}
.green { color: var(--green-400); }
.red { color: var(--red-400); }
button {
margin-top: 2rem;
padding: 10px;
width: 100%;
border: 1px solid var(--ui-300);
background-color: var(--green-400);
color: var(--text-100);
}
input {
border: 1px solid var(--ui-300);
background-color: var(--ui-100);
color: var(--text-100);
}
input:focus {
outline: none;
border-color: var(--cyan-400);
}
header {
margin-top: 4rem;
}
main {
margin: 4rem 0;
}
.flex-container {
display: flex;
flex-direction: row;
column-gap: 1rem;
}
.flex-1 {
order: 1;
width: 400px;
}
.flex-2 {
order: 2;
}