Javascript - Calculating monetary totals - javascript

I have a page on a site that calculates rate totals for a rental, which are optional. The javascript serves nothing more but to show you the updated total if you select or unselect checkbox options.
Here is what I've tried. It appears to work, but it is adding 2 cents to every other click for one of the checkboxes, I believe because toFixed rounds. How can I keep the number from rounding?
function update_rate(state, amount, field) {
if (state == true) {
rate = parseFloat($('span.rateTotal').text()) + amount;
rate = rate.toFixed(2);
due = parseFloat($('span.'+field).text()) + amount;
due = due.toFixed(2);
$('span.rateTotal').text(rate);
$('span.'+field).text(due);
} else {
rate = parseFloat($('span.rateTotal').text()) - amount;
rate = rate.toFixed(2);
due = parseFloat($('span.'+field).text()) - amount;
due = due.toFixed(2);
$('span.rateTotal').text(rate);
$('span.'+field).text(due);
}
}
Checkbox HTML:
<cfinput type="checkbox" name="linen_service" id="linen_service" checked="checked" value="0" onClick="update_rate(this.checked, #reservationQuery[7].xmlChildren[i]['dblAmount'].xmlText#, 'second-due');" />
Basically passes in the amount of the option, checkbox state, and the name of the field that should be affected.
Edit: Fiddle URL: http://jsfiddle.net/nGrVf/6/

Are you stuck with your your types?
I find it is almost always better to store monetary values in whole numbers of the lowest unit (e.g, cents, or pence) and then add the decimal point two places from the right when displaying.
So the conversions might look like:
var dollars = 100.43;
var cents = Math.round(dollars * 100);
//convert for display
var sCents = String(cents);
var display = sCents.substr(0, sCents.length-2) + "." + sCents.substr(sCents.length-2,2);
alert(display);
Can be seen here at http://jsfiddle.net/jameswiseman76/jhjtP/
Then you don't have to worry about any ugly floating point conversions.

The simplest solution I can think up is to round it down (using muls and floor): i.e.
rate = Math.floor(rate * 100)/100;
Edit: After a bit of testing, I found that there is nothing wrong with the given code. This means the calculation error comes from another part of the code or from the unknown amount variable.

Related

Calling a method on a variable and stopping a loop before reaching threshold

I'm reading Kyle Simpson's YDKJS and this was the first exercise after the first chapter. We were to write a program that finds the value of a phone + tax and purchase a certain amount of phones + accessories below a given threshold.
A couple of questions.
1) Is there a way to call the toFixed method on a var without having to build it into a function? Meaning can I build that formatting function then just call it on a variable when I'm going to console.log it.
2) If I place 'moneyInBank = moneyInBank - amount;' into the loop, why does it spit out a negative number? Does this have to deal with scopes? Is it creating a new variable inside the loop instead of changing the balance at the global level?
I wanted to go slightly beyond what was required like.
- Stop the loop if adding 1 more phone breaks the threshold. Instead, it went above the threshold. In this case, do I need to use some type of remainder expression?
const spendingThreshold = 1500;
const priceOfPhone = 150;
const priceOfAcc = 20;
const salesTax = .08;
var moneyInBank = 2500;
var amount = 0;
function tax(amount) {
return amount * salesTax;
}
function costOfPhone(){
return(priceOfPhone + priceOfAcc) + ((priceOfPhone + priceOfAcc) * salesTax);
}
/* We are running a conditional using the 'while' loop to run when the amount is less than the var spendingThreshold.
We then change the amount variable by adding 'amount' + the costOfPhone. costOfPhone takes in the price of the phone + taxes already, so there is no need for a taxes function. However, if we wanted to we could create a taxFunction
to be called on other products as well and not just on the phones.
*/
while (amount < spendingThreshold){
amount = amount + costOfPhone();
if(amount > spendingThreshold)
console.log("You cannot afford another phone!");
}
function formatAmount(){
return "$" + amount.toFixed(2);
}
moneyInBank = moneyInBank - amount;
function formatMoneyBank(){
return "$" + moneyInBank.toFixed(2);
}
console.log("The cost of each phone + tax is $" + costOfPhone());
console.log("The cost of your total purcahase is " + formatAmount(amount));
console.log("You have " + formatMoneyBank(moneyInBank) + " left in your bank account!");
For your question number one, you may display monetary values in javascript like so:
moneyInBank.toLocaleString('en', {style: "currency", currency: "USD"})
This works for all variables whose type is 'number' and leverages the internationalization capabilities of your browser. Of course, you could also define
const format = funds => `$${Number(funds).toFixed(2)}`
and invoke it like so format(5) or even so format("6.0").
For your second question, there is no issue with scopes. You just need to revise the logic. In particular, the loop does not seem to break when the threshold is exceeded and does not require the bank account to have sufficient funds moneyInBank >= amount.

Function ordering properly working?

I am new to HTML/CSS/JS and not understanding properly or am missing something completely. I am trying to update the Variable of "CitizenCost" by using Math.pow(2, NumCitizens) that part is working. The part that I am having trouble with is updating CitizenCost to the correct number and having the HTML represent it with the correct number. Right now it updates but it's always behind
IE: it should be X = 2^1 then X = 2^2 So on and so forth
However in the HTML it doesn't update accordingly it is always behind one equation so if the real cost is X = 2^2. The text will show X = 2^1. Unless the Player clicks the button again.
function buycitizen(){
CitizenCost = Math.pow(2, NumCitizens);
document.getElementById("CitizenCost").innerHTML = "Cost of Citizen " + CitizenCost;
if(land >= CitizenCost){
land = land - CitizenCost;
eps = eps + 2;
NumCitizens++;
document.getElementById("NumCitizens").innerHTML = NumCitizens;
}
else
document.getElementById("SystemMessage").innerHTML = "You do not have enough Land Resources to provide for more Citizens right now!";
setTimeout(systemclear, 5000)}
Here is a fiddle demonstrating the issue
https://jsfiddle.net/Traberjkt/yaq0rbad/
Here is the Git
https://github.com/Traberjkt/Conquest
I have tried setting a timer so the text of CitizenCost updates every second. I have thought and tried putting the cost equation in a separate function and re-locating it somewhere else in the function. Sadly I have had no luck figuring this out. Any help or pointing me in the right direction would be greatly appreciated!
Maybe you should make a new function called moreCitizens(howMany):
function moreCitizens(howMany) {
NumCitizens += howMany;
document.getElementById("NumCitizens").innerHTML = NumCitizens;
CitizenCost = Math.pow(2, NumCitizens);
document.getElementById("CitizenCost").innerHTML = "Cost of Citizen " + CitizenCost;
}
Then instead of
NumCitizens++;
you can just write
moreCitizens(1);
and that will update the cost and count fields.

javascript arrays reading in form data best practice

I'm still quite new to javascript and was wondering if there's a more efficient way to handle this situation, for example by using an array?
I have an HTML form with 6 fields that let you enter the last six weeks amount of overtime paid. I'm then using javascript to add the six values up, divide by six and multiply by 52 to obtain an annual overtime amount. My fields are named w_ot_1, w_ot_2 up to w_ot_6 and I'm using the code below. It all works fine but I'm finding it's really repetitive and it's not just overtime I need to run this calculation on. I'm sure there's got to be a more efficient way. Does anyone have any ideas that could help?
var weekly_ot_1 = parseFloat(document.getElementById("w_ot_1").value.replace(/[^0-9.]/g, ''));
var weekly_ot_2 = parseFloat(document.getElementById("w_ot_2").value.replace(/[^0-9.]/g, ''));
var weekly_ot_3 = parseFloat(document.getElementById("w_ot_3").value.replace(/[^0-9.]/g, ''));
var weekly_ot_4 = parseFloat(document.getElementById("w_ot_4").value.replace(/[^0-9.]/g, ''));
var weekly_ot_5 = parseFloat(document.getElementById("w_ot_5").value.replace(/[^0-9.]/g, ''));
var weekly_ot_6 = parseFloat(document.getElementById("w_ot_6").value.replace(/[^0-9.]/g, ''));
//weekly annualised overtime values
document.getElementById("w_annual_ot").value= addCommas((((weekly_ot_1 + weekly_ot_2 + weekly_ot_3 + weekly_ot_4 + weekly_ot_5 + weekly_ot_6)/6) * 52).toFixed(2));
This is a situation where you can leverage a simple for loop and string concatenation when calling document.getElementById(). I would suggest creating a function to calculate the overtime paid and have the function take the number of weeks as a parameter so that you can easily change it if you add more fields.
function getOvertimePaid(numberOfWeeks) {
var total = 0;
// Iterate from 1 to the number of weeks and increment the total by
// the parsed value in the field for the current index
for (var i=1; i<=numberOfWeeks; i++) {
total += parseFloat(document.getElementById('w_ot_' + i).value.replace(/[^0-9.]/g, '');
}
// Return the annualized amount as a float for flexibility
return (total / numberOfWeeks) * 52;
}
// Update weekly annualised overtime values and provide formatting at this point
document.getElementById("w_annual_ot").value= addCommas(getOvertimePaid(6).toFixed(2));
Another thing you may want to look at to make the code and the supporting HTML even more flexible is to leverage a class name on your weekly overtime input elements. If you do that and adjust the code slightly you can add or remove fields at will and the function to calculate the annualized overtime will continue to work. As an example:
HTML
<input type="text" id="w_ot_1" class="weekly-overtime" value="0.00" />
<input type="text" id="w_ot_2" class="weekly-overtime" value="0.00" />
<input type="text" id="w_ot_3" class="weekly-overtime" value="0.00" />
JavaScript
function getAnnualizedValue(className) {
// Get all elements with the given class name
var elements = document.getElementsByClassName(className);
// Iterate the elements and keep a running total of their values
var total = 0;
for (var i = 0; i < elements.length; i++) {
total += parseFloat((elements[i].value || '0').replace(/[^0-9.]/g, '');
}
// Return the annualized amount as a float for flexibility
return (total / numberOfWeeks) * 52;
}
// Update weekly annualised overtime values and provide formatting at this point
document.getElementById("w_annual_ot").value= addCommas(getAnnualizedValue('weekly-overtime').toFixed(2));

Two sliders which are connected to give other values

Trying to create a loan calculator based on two sliders. I feel I'm nearly there but the syntax is letting me down plus one last formula.
I have two sliders, one which represents the loan amount, the other represents the loan length.
Here is a bit of the code to highlight all the calculations. I think there are errors here too.
function update() {
$amount = $("#slider1").slider("values", 100);
$interest = $amount / 100 * 15 ;
$perday = 15 ;
$apr = (($interest / $amount) / ("#slider2"/365) * 10000) / 100;
$amount2 = $amount + $interest;
$("#amount").val($amount1);
$("#amount2").val($amount2);
$("#amount3").val($interest);
}
Interest is charged at 15% of the amount borrowed.
Each day is worth 15p so in order to get my final charge.
[15% of the loan amount total]
– [0.15p per day credit]
I have developed a fiddle but its not correct, hence why I'm here.
Fiddle Here
How can I get both the sliders to work together so that if I move the top or bottom slider, it will affect the overall loan amount and Interest?
Any help will be most appreciated. I'm really struggling with this one.
I believe this is what you want - jsfiddle <- follow link
function update() {
$interest = 0.15 ;
$perday = 15 ;
$amount1 = $("#amount").val();
$dayscount = $("#days").val();
$amount2 = parseInt($amount1) + $interest * parseInt($amount1) + (parseInt($dayscount) * ($perday/100));
$("#amount2").val($amount2);
$("#amount3").val(parseFloat($amount2-$amount1).toFixed(2));
}
Fixed your update algorithm and your slider handlers
I considered that you last 2 fields (Your Loan & Interest) are the final value to be paid and the difference between the value to be paid and the value borrowed respectively. If this interpretation is not what you intended please comment.
UPDATE 1
Here I updated the jsfiddle. Beware that I don't know what the APR is, so validate that my calculation are right. I also did not use any rounding, cause I don't know if you need it like this
UPDATE 2
Updated with new formula here. I still have no idea if this is right or not
If i understood you right, you could do something like this:
function update() {
var interest = 0.15;
var sliderAmount = parseFloat($("#slider1").slider("option", "value"));
var days = parseFloat($("#slider2").slider("option", "value"));
var perday = 15 * days ;
var interestAmount = sliderAmount * interest * days;
$("#amount1").val(sliderAmount);
$("#amount2").val(sliderAmount);
$("#amount3").val(interestAmount+ perday);
}

2 text box need to verify they are not empty or contain 0

I have a function with info that grabs hours, rates, and then tax deduction and then spits it out. It works fine
var newtax= new Number(dep[i]);
taxrate = newtax*100;
var h=eval(document.paycheck.hours.value);
var r=eval(document.paycheck.payrate.value);
document.paycheck.feedback.value= taxrate + txt;
var total= r*(1-newtax)*h ;
total=total.toFixed(2);
document.paycheck.feedback3.value= ("$ "+ total);
I have to put where it takes the total and puts it in a function to put it only two decimals. It works this way and only does two decimals but i need the decimal conversion in a function. can anyone shed some like .
This is where i cut it to two decimals and i am unable to put in function and then send it back to the feedback3.value.
total=total.toFixed(2);
document.paycheck.feedback3.value= ("$ "+ total);
If you're asking how to write a function that takes a number and formats it as a dollar value with two decimals (as a string) then this would work:
function formatMoney(num) {
return "$ " + num.toFixed(2);
}
// which you could use like this:
document.paycheck.feedback3.value= formatMoney(total);
// though you don't need the total variable (unless you use it elsewhere)
// because the following will also work:
document.paycheck.feedback3.value = formatMoney( r*(1-newtax)*h );
By the way, you don't need eval to get the values from your fields. Just say:
var h = document.paycheck.hours.value;
var r = document.paycheck.payrate.value;

Categories