Build a Cash Register Project - Build a Cash Register

Tell us what’s happening:

My cash register project code is admittedly messy, but it should technically pass all the tests. And it is passing all of them but one: question 17:

  1. When price is less than the value in the #cash element, total cash in drawer cid is greater than change due, but the individual denomination amounts make it impossible to return needed change, when the #purchase-btn element is clicked, the value in the #change-due element should be "Status: INSUFFICIENT_FUNDS”

When I run the code with this exact scenario, the status message does indeed appear as it should. So I don’t understand why the test won’t pass? Does anyone have any ideas?

Including my code below:

Your code so far

<!-- file: index.html -->

/* file: script.js */
//// UI variables
const changeDueDisplay = document.getElementById("change-due");
const purchaseBtn = document.getElementById("purchase-btn");
const totalDisplay = document.getElementById("total");
const currencyAmounts = document.getElementsByClassName("currency-amounts");

//// other variables

// price of product customer wants to buy
let price = 19.5;

// cash in drawer
let cid = [
  ['PENNY', 0.5],
  ['NICKEL', 0],
  ['DIME', 0],
  ['QUARTER', 0],
  ['ONE', 0],
  ['FIVE', 1],
  ['TEN', 1],
  ['TWENTY', 1],
  ['ONE HUNDRED', 01]
];
const denominationsIC = [1, 5, 10, 25, 100, 500, 1000, 2000, 10000];

// display cost of item on page (in "total" element)
totalDisplay.textContent = `Total: $${price}`;
// display current cash in drawer
for (let i = 0; i < 9; i++) {
  currencyAmounts[i].textContent = `$${Math.round(cid[i][1] * 100) / 100}`;
}

//////////

const main = (cash) => {
  // multiply by 100 to avoid rounding errors; IC stands for "in cents"
  const cashIC = cash * 100; // amount received from customer
  const priceIC = price * 100; // cost of item they want to buy
  const diffIC = cashIC - priceIC; // amount of change we owe them
  const totalCIDIC = (cid[0][1] + cid[1][1] + cid[2][1] + cid[3][1] + cid[4][1] + cid[5][1] + cid[6][1] + cid[7][1] + cid[8][1]) * 100; // all the money we have available
  // the total change we will give them, by denomination
  let changeDue = [];
  // variable to track cash register status
  let status = "";

  if (cashIC < priceIC) {
    // If the customer does not have enough money for the purchase, log that
    alert("Customer does not have enough money to purchase the item");

  } else if (cashIC === priceIC) {
    // If the customer paid with exact change, log that
    status = "No change due - customer paid with exact cash";

  } else {
    // Otherwise, the customer is owed a certain amount of change
    if (diffIC > totalCIDIC) {
      // If we don't have enough money in the register, set status to insufficient
      status = "Status: INSUFFICIENT_FUNDS";
      changeDue = false;
    } else if (diffIC === totalCIDIC) {
      // If we have exactly enough and no more, set status to closed
      status = "Status: CLOSED";
      changeDue = calculate(diffIC);
    } else {
      // If we have more than enough, calculate change due
      changeDue = calculate(diffIC);
      if (!changeDue) {
        status = "Status: INSUFFICIENT_FUNDS";
      } else if (totalCIDIC === 0) {
        status = "Status: CLOSED";
      } else {
        status = "Status: OPEN";
      }
    }
  }
  updateStatus(status);
  updateDisplay(changeDue);
}

const calculate = (diffIC) => {
  // variables to track calculation
  const totalChangeDueIC = diffIC;
  let remainderOwedIC = diffIC;
  let cashToHandBackIC = 0;
  let denominationsToHandBack = [];
  let howMany = 0;
  // time to calculate!
  while (cashToHandBackIC < totalChangeDueIC) {
    // loop through cid, largest denomination to smallest
    for (let i = 8; i >= 0; i--) {
      // if that DENOMINATION is < remainderOwedIC AND cash in this drawer is > 0:
      if ((denominationsIC[i] <= remainderOwedIC) && (cid[i][1] > 0)) {
        // first check if we've reached the pennies & there aren't enough
        if (i === 0 && cid[0][1] * 100 < remainderOwedIC && cashToHandBackIC === 0) {
          // if so, change variables as necessary and return early
          denominationsToHandBack = [];
          return false;
        }
        // ...then WHILE both those things remain true:
        while ((denominationsIC[i] <= remainderOwedIC) && (cid[i][1] > 0)) {
          // add 1x that denomination to the cash to hand back
          cashToHandBackIC += denominationsIC[i];
          // and subtract 1x that denomination from the remainder still owed
          remainderOwedIC -= denominationsIC[i];
          // also subtract it from that drawer of the cash register!
          cid[i][1] -= denominationsIC[i] / 100;
          // track how many of this denomination is needed
          howMany++;
        }
      }
      // add how many of that denomination were needed to the hand-back array
      denominationsToHandBack[i] = howMany;
      // and reset the "how many" tracker before moving to the next denomination
      howMany = 0;
    }
  }
  if (denominationsToHandBack.length > 0) {
    return denominationsToHandBack;
  } else {
    return false;
  }
}

const updateStatus = (status) => {
  // show status message as first p child of changeDue div
  changeDueDisplay.innerHTML += `<p>${status}</p>`;
}

const updateDisplay = (changeArray) => {
  if (changeArray) {
      // loop through changeArray; for each item...
    for (let i = 8; i >= 0; i--) {
      // ...if the value is more than zero...
      if (changeArray[i] > 0) {
        // ...add p child showing it
        changeDueDisplay.innerHTML += `<p>${cid[i][0]}: $${changeArray[i] * denominationsIC[i] / 100}</p>`;
      }
    }
    // also update each cash register drawer to show the new amount
    for (let i = 0; i < 9; i++) {
      currencyAmounts[i].textContent = `$${Math.round(cid[i][1] * 100) / 100}`;
    }
  }
}

purchaseBtn.addEventListener("click", () => {
  // reset changeDue element to empty at start
  changeDueDisplay.innerHTML = "";
  // get value of cash received as payment from customer
  let cash = document.getElementById("cash").value;
  // run main function
  main(cash);
  // reset input field to empty
  document.getElementById("cash").value = "";
});
/* file: styles.css */

Your browser information:

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:135.0) Gecko/20100101 Firefox/135.0

Challenge Information:

Build a Cash Register Project - Build a Cash Register

@ChiselD Can your provide your code for the HTML file?

Hi soryaek, thanks for the reply! Yes, of course, I should have thought of that:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Cash Register</title>
	<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>

	<div id="app">

		<header>
			<h1>Cash Register</h1>
		</header>

		<div id="give-and-take">

			<div id="change-due"></div>

			<div id="cash-received">
				<p>Enter cash from customer:</p>
				<input id="cash" type="number" name="cash">
			</div>

			<button id="purchase-btn">Purchase</button>

		</div> <!-- give-and-take -->

		<div id="register">

			<div id="total-container">
				<div id="total">Total: $0</div>
			</div>

			<div id="drawer">
				<h3>Change in drawer:</h3>
				<p class="currency">
					<span class="currency-names">Pennies:</span>
					<span class="currency-amounts">$1.01</span>
				</p>
				<p class="currency">
					<span class="currency-names">Nickels:</span>
					<span class="currency-amounts">$2.05</span>
				</p>
				<p class="currency">
					<span class="currency-names">Dimes:</span>
					<span class="currency-amounts">$3.10</span>
				</p>
				<p class="currency">
					<span class="currency-names">Quarters:</span>
					<span class="currency-amounts">$4.25</span>
				</p>
				<p class="currency">
					<span class="currency-names">Ones:</span>
					<span class="currency-amounts">$90</span>
				</p>
				<p class="currency">
					<span class="currency-names">Fives:</span>
					<span class="currency-amounts">$55</span>
				</p>
				<p class="currency">
					<span class="currency-names">Tens:</span>
					<span class="currency-amounts">$20</span>
				</p>
				<p class="currency">
					<span class="currency-names">Twenties:</span>
					<span class="currency-amounts">$60</span>
				</p>
				<p class="currency">
					<span class="currency-names">Hundreds:</span>
					<span class="currency-amounts">$100</span>
				</p>
			</div>

		</div> <!-- register -->

	</div> <!-- app -->
	

<script type="text/javascript" src="script.js"></script>
</body>
</html>

Is this your current code? When I paste it in no tests pass and I get a fatal error immediately.

  1. You should have the HTML file link to the JavaScript file.
SyntaxError: unknown: Legacy octal literals are not allowed in strict mode. (23:18)

  21 |   ['TEN', 1],
  22 |   ['TWENTY', 1],
> 23 |   ['ONE HUNDRED', 01]

Hi pkdvalis, thanks for the response. You’re right, that value for the hundreds obviously wasn’t correct, but it also wasn’t the only problem here: I’ve been testing the code with all sorts of different values for the “cid” variable, including the ones suggested by the tests, and even when the “cid” values are unproblematic, it still won’t pass.

I’ve been working on it some more since then, and I got the app completely functional without a UI (in the JavaScript console only). I can also manually pass all the tests for the challenge. But the automated testing process still tells me I’m not passing… so weird.

Here’s the latest code that passed all the tests manually but not automatically:

HTML:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Cash Register</title>
	<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>

	<div id="app">

		<header>
			<h1>Cash Register</h1>
		</header>

		<div id="give-and-take">

			<div id="change-due"></div>

			<div id="cash-received">
				<p>Enter cash from customer:</p>
				<input id="cash" type="number" name="cash">
			</div>

			<button id="purchase-btn">Purchase</button>

		</div> <!-- give-and-take -->

		<div id="register">

			<div id="total-container">
				<div id="total">Total: $0</div>
			</div>

			<div id="drawer">
				<h3>Change in drawer:</h3>
				<p class="currency">
					<span class="currency-names">Pennies:</span>
					<span class="currency-amounts">$1.01</span>
				</p>
				<p class="currency">
					<span class="currency-names">Nickels:</span>
					<span class="currency-amounts">$2.05</span>
				</p>
				<p class="currency">
					<span class="currency-names">Dimes:</span>
					<span class="currency-amounts">$3.10</span>
				</p>
				<p class="currency">
					<span class="currency-names">Quarters:</span>
					<span class="currency-amounts">$4.25</span>
				</p>
				<p class="currency">
					<span class="currency-names">Ones:</span>
					<span class="currency-amounts">$90</span>
				</p>
				<p class="currency">
					<span class="currency-names">Fives:</span>
					<span class="currency-amounts">$55</span>
				</p>
				<p class="currency">
					<span class="currency-names">Tens:</span>
					<span class="currency-amounts">$20</span>
				</p>
				<p class="currency">
					<span class="currency-names">Twenties:</span>
					<span class="currency-amounts">$60</span>
				</p>
				<p class="currency">
					<span class="currency-names">Hundreds:</span>
					<span class="currency-amounts">$100</span>
				</p>
			</div>

		</div> <!-- register -->

	</div> <!-- app -->
	

<script type="text/javascript" src="script.js"></script>
</body>
</html>

JavaScript:

//// UI variables
const changeDueDisplay = document.getElementById("change-due");
const purchaseBtn = document.getElementById("purchase-btn");
const totalDisplay = document.getElementById("total");
const currencyAmounts = document.getElementsByClassName("currency-amounts");

//// other variables

// cash provided by customer to pay with
let cash = 16.50;
// price of product customer wants to buy
let price = 10;
// cash in drawer
let cid = [
  ['PENNY', 1.01],
  ['NICKEL', 2.05],
  ['DIME', 3.1],
  ['QUARTER', 4.25],
  ['ONE', 90],
  ['FIVE', 55],
  ['TEN', 20],
  ['TWENTY', 60],
  ['ONE HUNDRED', 100]
];

// status message to show
let status = "";

const denominationsInCents = [1, 5, 10, 25, 100, 500, 1000, 2000, 10000];

// total amount of money per drawer in cents (pennies come first)
let centsPerDrawer = [];
for (let i = 0; i < cid.length; i++) {
  centsPerDrawer.push(Math.round(cid[i][1] * 100));
}

// total cash available in cash register, in cents
let totalCIDIC = 0;
for (let i = 0; i < centsPerDrawer.length; i++) {
  totalCIDIC += centsPerDrawer[i];
}

///////////////////////////////////////////////////

// display cost of item on page (in "total" element)
totalDisplay.textContent = `Total: $${price}`;

// display current cash in drawer
const updateCidDisplay = () => {
  for (let i = 0; i < 9; i++) {
    currencyAmounts[i].textContent = `$${Math.round(cid[i][1] * 100) / 100}`;
  }
}
updateCidDisplay();

const main = (cash) => {
  if (cash < price) {
    // sit1
    alert("Customer does not have enough money to purchase the item");
    return;
  } else if (cash === price) {
    // sit2
    status = "No change due - customer paid with exact cash";
    showStatus(status);
    return;
  } else {
    // second comparison
    const changeDue = (cash - price).toFixed(2);
    checkDrawer(changeDue);
  }
}

const checkDrawer = (changeDue) => {
  let denomsDue = [];
  // convert changeDue to cents to avoid rounding errors
  let changeDueIC = Math.round(changeDue * 100);
  // carry out second comparison
  if (changeDueIC > totalCIDIC) {
    // sit3: customer should receive change but we don't have enough
    status = "Status: INSUFFICIENT_FUNDS";
    showStatus(status);
    return;
  } else if (changeDueIC === totalCIDIC) {
    // sit4: customer should receive change; we have exactly enough & no more
    status = "Status: CLOSED";
    calculate(changeDue, changeDueIC);
    return;
  } else {
    // sit5: customer should receive change; we have more than enough
    calculate(changeDue);
    return;
  }
}

const calculate = (changeDue) => {
  // array to track denoms to hand back to customer:
  let toHandBack = [];
  // We owe 460 cents change ($4.60)
  let remainingChangeDueIC = Math.round(changeDue * 100);
  // Start with largest denominations
  for (let i = 8; i >= 0; i--) {
    // Set up howMany variable to track how many of this denom we will take
    let howMany = 0;
    // If this denom (min amount avail in this drawer) is smaller than the amount owed...
    // ...and the total amount available in the drawer is greater than zero:
    if (denominationsInCents[i] <= remainingChangeDueIC && centsPerDrawer[i] > 0) {
      // While amount we still owe is >= this denom AND amount is drawer is > 0...
      while (remainingChangeDueIC >= denominationsInCents[i] && centsPerDrawer[i] > 0) {
        // Plus-one the 'howMany' variable that tracks how many of this denom we need
        howMany++;
        // Take one denom from this drawer (drawer total -= one denom in cents)
        centsPerDrawer[i] -= denominationsInCents[i];
        // Also subtract that amount from the totalCIDIC
        totalCIDIC -= denominationsInCents[i];
        // And subtract one denom in cents from the amount we still owe
        remainingChangeDueIC -= denominationsInCents[i];
      }
      // When done, add (push) that amount & denom to "toHandBack" array
      toHandBack.push([cid[i][0], (howMany * denominationsInCents[i]) / 100]);
      // And subtract the total amount taken of that denom from its CID drawer
      cid[i][1] -= (howMany * denominationsInCents[i]) / 100;
    }
  }
  // If after all this we still owe cash...
  if (remainingChangeDueIC > 0) {
    status = "Status: INSUFFICIENT_FUNDS";
    showStatus(status);
  } else if (totalCIDIC === 0) {
    status = "Status: CLOSED";
    showResults(status, toHandBack);
  } else {
    status = "Status: OPEN";
    showResults(status, toHandBack);
  }
  return;
}

const showStatus = (status) => {
  // show status message as first child of changeDue div
  changeDueDisplay.innerHTML += `${status}`;
  return;
}

const showResults = (status, denomsDue) => {
  // Update the cash register element to show how much cash remains in the drawer
  updateCidDisplay();
  showStatus(status);
  for (let i = 0; i < denomsDue.length; i++) {
    // ...add p child showing it
    changeDueDisplay.innerHTML += `<p>${denomsDue[i][0]}: $${denomsDue[i][1]}</p>`;
  }
  return;
}

purchaseBtn.addEventListener("click", () => {
  // reset changeDue element to empty at start
  changeDueDisplay.innerHTML = "";
  // get value of cash received as payment from customer
  cash = parseFloat(document.getElementById("cash").value);
  // run main function
  main(cash);
  // reset input field to empty
  document.getElementById("cash").value = "";
});

Could the problem be something as simple as the HTML display on the page, perhaps? I noticed that each row of my “results display” has an empty row after it (presumably due to each row being a separate

element), and this is not the way it looks in the sample app.

Sample app display:

Status: CLOSED
FIVE: $5
ONE: $1
PENNY: $0.5

My display:

Status: CLOSED

FIVE: $5

ONE: $1

PENNY: $0.5

You need to get rid of these global variables - your code needs to be able to run multiple times in a row.

Man, you’re a lifesaver. I had no idea that adding my own global variables would cause problems repeating the tests. Well, TIL. Thank you so much, this solved it!

1 Like

Explanation of the issue with global variables if you are interested

Your code contains global variables that are changed each time the function is run. This means that after each function call completes, subsequent function calls start with the previous value. To fix this, make sure your function doesn’t change any global variables, and declare/assign variables within the function if they need to be changed.

Example:

var myGlobal = [1];
function returnGlobal(arg) {
  myGlobal.push(arg);
  return myGlobal;
} // unreliable - array gets longer each time the function is run

function returnLocal(arg) {
  var myLocal = [1];
  myLocal.push(arg);
  return myLocal;
} // reliable - always returns an array of length 2