Build a Cash Register Project - Build a Cash Register

Tell us what’s happening:

Hi everyone,

I’ve been working on the Cash Register Project for over 15 hours now, and I’m stuck on two failing tests (Tests 18 & 19). Despite the app displaying exactly what’s expected visually, FCC’s automated tests aren’t passing.

I’ve refactored extensively, handled floating-point precision using integer math (multiplying by 100), ensured denominations are dispensed from highest to lowest, and formatted currency using two decimal places. Still, Tests 18 and 19 refuse to pass.

Your code so far

<!-- file: index.html -->
<header>
  <h1>Cash Register Project</h1>
</header>
<div id="change-due" class="change-due"></div>
<form>
  <label for="cash">Enter cash from customer</label>
  <input id="cash" type="number" name="cashAmt">
  <button type="button" id="purchase-btn">Purchase</button>
</form>
/* file: script.js */
// Key global variables
const changeMade = {
  "ONE HUNDRED": 0, TWENTY: 0, TEN: 0, FIVE: 0, ONE: 0,
  QUARTER: 0, DIME: 0, NICKEL: 0, PENNY: 0
};
const changeDue = document.getElementById("change-due");

// ProcessTransaction & DivideChange (core logic)
const ProcessTransaction = (cash, price) => {
  let changeOwed = Math.round((cash - price) * 100);
  DivideChange(changeOwed);
  if (notEnough) {
    StatusMessage(errMsg);
  } else {
    GiveCustomerChange();
  }
};

const DivideChange = (changeOwed) => {
  tempCID = cid.map(([name, amount]) => [name, Math.round(amount * 100)]);
  if (changeOwed === Math.round(cid.reduce((a, b) => a + b[1], 0) * 100)) {
    isClosed = true;
  }
  while (changeOwed > 0) {
    let madeChange = false;
    // Loop through denominations and subtract amounts
    // (Omitting full denomination loop for brevity)
    if (!madeChange) {
      notEnough = true;
      break;
    }
  }
  if (changeOwed === 0) {
    cid = tempCID.map(([name, amount]) => [name, amount / 100]);
  }
};

// Status message display (suspected problem area)
const GiveCustomerChange = () => {
  changeDue.innerHTML = "";
  if (isClosed) {
    StatusMessage("Status: CLOSED");
  } else {
    StatusMessage("Status: OPEN");
  }
  for (const denomination in changeMade) {
    if (changeMade[denomination] !== 0) {
      const el = document.createElement("p");
      el.textContent = `${denomination}: $${changeMade[denomination] / 100}`;
      changeDue.appendChild(el);
      changeMade[denomination] = 0;
    }
  }
};

const StatusMessage = (msg) => {
  changeDue.textContent = msg;
};

const hasEnoughChange = (index, amt) => tempCID[index][1] >= amt;
/* file: styles.css */

Your browser information:

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36

Challenge Information:

Build a Cash Register Project - Build a Cash Register

Can you post the full code of both HTML and JavaScript?

as the JavaScript file is not linked to your HTML and doesn’t have the price and cid global variables.

Sure! Here is all of my HTML and JavaScript source code. Please let me know if there is anything else that you need! Thank you so much!

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, intitial-scale=1.0">
    <title>Cash Register Project</title>
    <link rel="stylesheet" href="./styles.css">
  </head>
  <body>
    <div class="container">
      <header>
        <h1>Cash Register Project</h1>
      </header>
      <div id="change-due" class="change-due"></div>
      <form>
        <label for="cash">Enter cash from customer</label>
        <input id="cash" type="number" name="cashAmt">
        <button type="button" id="purchase-btn">Purchase</button>
      </form>

      <div id="cash-register" class="cash-register">
        <div id="screen-border" class="screen-border">
          <div id="screen" class="screen"><p>Total: $<span id="amount-due" class="amount-due">0</span></p></div>
        </div>
        <div id="neck" class="neck"></div>
        <div id="body" class="body">
          <div id="cash-in-drawer" class="cash-in-drawer">
            <p>Pennies: <span class="denomination">$1.01</span></p>
            <p>Nickels: <span class="denomination">$2.05</span></p>
            <p>Dimes: <span class="denomination">$3.10</span></p>
            <p>Quarters: <span class="denomination">$4.25</span></p>
            <p>Ones: <span class="denomination">$90</span></p>
            <p>Fives: <span class="denomination">$55</span></p>
            <p>Tens: <span class="denomination">$20</span></p>
            <p>Twenties: <span class="denomination">$60</span></p>
            <p>Hundreds: <span class="denomination">$100</span></p>
          </div>
          <div id="buttons" class="buttons"></div>
        </div>
        <div id="cash-drawer" class="cash-drawer"></div>
      </div>
      <!-- <div id="change-left" class="change-left"></div> -->
    </div>
    <script src="./script.js"></script>
  </body>
</html>
let price = 19.5;
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]
];

// let cid = [
//   ['PENNY', 0.5],
//   ['NICKEL', 0],
//   ['DIME', 0],
//   ['QUARTER', 0],
//   ['ONE', 0],
//   ['FIVE', 0],
//   ['TEN', 0],
//   ['TWENTY', 0],
//   ['ONE HUNDRED', 0]
// ];

let tempCID = [];

const changeMade = {
  "ONE HUNDRED": 0,
  TWENTY: 0,
  TEN: 0,
  FIVE: 0,
  ONE: 0,
  QUARTER: 0,
  DIME: 0,
  NICKEL: 0,
  PENNY: 0,
};

const denominationOrder = [
  "ONE HUNDRED",
  "TWENTY",
  "TEN",
  "FIVE",
  "ONE",
  "QUARTER",
  "DIME",
  "NICKEL",
  "PENNY"
]

const amountDueSpan = document.getElementById("amount-due");
const buttonsContainer = document.getElementById("buttons");
const cashFromCustomer = document.getElementById("cash");
const changeDue = document.getElementById("change-due");
const purchaseBtn = document.getElementById("purchase-btn");
const denominations = document.querySelectorAll(".denomination");
const buttons = [];

let isClosed = false;
let notEnough = false;
const errMsg = "Status: INSUFFICIENT_FUNDS";
amountDueSpan.textContent = price;

const ProcessTransaction = (cash, price) => {
  if (cash < price) {
    alert("Customer does not have enough money to purchase the item");
  }
  
  else if (cash === price) {
    StatusMessage("No change due - customer paid with exact cash");
  }

  else {
    let changeOwed = Math.round((cash-price) * 100);
    
    DivideChange(changeOwed);

    if (notEnough) {
      StatusMessage(errMsg);
    }
    else {
      UpdateCashRegister();
      GiveCustomerChange();
    }
    
  }

};

const DivideChange = (changeOwed) => {
  const totalCashInDrawer = Math.round((cid.reduce((a, b) => a + b[1], 0)) * 100);

  tempCID = cid.map(([name, amount]) => [name, Math.round(amount * 100)]);

  if (changeOwed > totalCashInDrawer) {
    notEnough = true;
  }

  else {
    if (changeOwed === totalCashInDrawer) {
      isClosed = true;
    }

    let madeChange = false;
    while (changeOwed > 0) {
      console.log(changeOwed/100);
      
      madeChange = false;
      if (changeOwed >= 10000) {
        if (hasEnoughChange(8, 10000)) {
          changeOwed -= 10000;
          tempCID[8][1] -= 10000;
          changeMade["ONE HUNDRED"] += 10000;
          madeChange = true;
          continue;
        }
      }

      if (changeOwed >= 2000) {
        if (hasEnoughChange(7, 2000)) {
          changeOwed -= 2000;
          tempCID[7][1] -= 2000;
          changeMade["TWENTY"] += 2000;
          madeChange = true;
          continue;
        }
      }

      if (changeOwed >= 1000) {
        if (hasEnoughChange(6, 1000)) {
          changeOwed -= 1000;
          tempCID[6][1] -= 1000;
          changeMade["TEN"] += 1000;
          madeChange = true;
          continue;
        }
      }

      if (changeOwed >= 500) {
        if (hasEnoughChange(5, 500)) {
          changeOwed -= 500;
          tempCID[5][1] -= 500;
          changeMade["FIVE"] += 500;
          madeChange = true;
          continue;
        }
      }

      if (changeOwed >= 100) {
        if (hasEnoughChange(4, 100)) {
          changeOwed -= 100;
          tempCID[4][1] -= 100;
          changeMade["ONE"] += 100;
          madeChange = true;
          continue;
         }
      }

      if (changeOwed >= 25) {
        if (hasEnoughChange(3, 25)) {
          changeOwed -= 25;
          tempCID[3][1] -= 25;
          changeMade["QUARTER"] += 25;
          madeChange = true;
          continue;
        }
      }

      if (changeOwed >= 10) {
        if (hasEnoughChange(2, 10)) {
          changeOwed -= 10;
          tempCID[2][1] -= 10;
          changeMade["DIME"] += 10;
          madeChange = true;
          continue;
        }
      }

      if (changeOwed >= 5) {
        if (hasEnoughChange(1, 5)) {
          changeOwed -= 5;
          tempCID[1][1] -= 5;
          changeMade["NICKEL"] += 5;
          madeChange = true;
          continue;
        }
      }

      if (changeOwed >= 1) {
        if (hasEnoughChange(0, 1)) {
          changeOwed -= 1;
          tempCID[0][1] -= 1;
          changeMade["PENNY"] += 1;
          madeChange = true;
          continue;
        }
      }

      if (!madeChange) {
        notEnough = true;
        break;
      }
    }
    if (changeOwed === 0) {
      cid = tempCID.map(([name, amount]) => [name, amount/100]);
      console.log(cid);
    }
  }
};

const UpdateCashRegister = () => {
  denominations.forEach((denom, index) => {
    denom.textContent = `$${cid[index][1]}`;
  });
};

const GiveCustomerChange = () => {
  changeDue.innerHTML = "";
  
  if (isClosed) {
    StatusMessage("Status: CLOSED");
    
  }
  else {
    StatusMessage("Status: OPEN");
  }
  
  for (const denomination in changeMade) {
    if (changeMade[denomination] !== 0) {
      const el = document.createElement("p");
      el.textContent = `${denomination}: $${changeMade[denomination]/100}`;
      changeDue.appendChild(el);
      changeMade[denomination] = 0; // reset change for next transaction
    }
  }
  
};

const StatusMessage = (msg) => {
  changeDue.textContent = msg;
};

const hasEnoughChange = (index, amt) => tempCID[index][1] >= amt;

//*============ ENTRY POINT ============*//
UpdateCashRegister();
for (let i = 12; i > 0; i--) {
  const button = document.createElement("button");
  if (i === 3) {
    button.textContent = "Del"
    button.setAttribute("id", "buttonDel");
  }
  else if (i === 2) {
    button.textContent = "0";
    button.setAttribute("id", "button0");
  }
  else if (i === 1) {
    button.textContent = "."
    button.setAttribute("id", `buttonDot`);
  }
  else {
    button.textContent = i - 3;
    button.setAttribute("id", `button${i-3}`);
  }
  button.setAttribute("type", "button");
  button.classList.add("button");
  buttonsContainer.appendChild(button);
  buttons.push(button);
}

buttons.forEach((button) => {
  button.addEventListener("click", ({target}) => {
    const buttonNumber = target.id.slice(6);
    if (Number(buttonNumber) || buttonNumber === "0") {
      if (amountDueSpan.textContent === "0") {
        amountDueSpan.textContent = buttonNumber;
        price = buttonNumber;
      }
      else {  
        amountDueSpan.textContent += buttonNumber;
        price += buttonNumber;
      }
      
    }
    else {
      if (buttonNumber === "Del") {
        let currentNumber = amountDueSpan.textContent;
        if (currentNumber !== "0") {
          amountDueSpan.textContent = currentNumber.slice(0, currentNumber.length-1);
          price = currentNumber.slice(0, currentNumber.length-1);;
        }
        if (currentNumber.length === 1) {
          amountDueSpan.textContent = "0";
          price = "0";
        }    
      }
      else {
        if (!amountDueSpan.textContent.includes(".")) {
          amountDueSpan.textContent += ".";
          price += ".";
        }
      } 
    }
  });
});

purchaseBtn.addEventListener("click", () => {
  const cash = Number(cashFromCustomer.value);
  if (cash) {
    changeDue.style.display = "block";
    ProcessTransaction(cash, Number(price));
  }
  else {
    alert("Please enter an amount.");
  }
});

cashFromCustomer.addEventListener("keydown", ({key}) => {
  if (key === "Enter") {
    const cash = Number(cashFromCustomer.value);
    if (cash) {
      changeDue.style.display = "block";
      ProcessTransaction(cash, Number(price));
    }
  }
})

one thing I immediately notice, is your use of global variables.
You need to move all your globals like these ones:

to be local variables (and pass them around as arguments to functions as needed)

This is because the way the tests run, they call your functions in sequence and they never reload the page so all your globals will keep their values from the previous run(s).

ps. you can keep the static global variables that refer to the UI or that are values that never change, but anything else should be removed from the global scope.

Thank you!

By making those two variables local within ProcessTransaction() (along with the object literal changeMade), I was able to pass the tests!

THANK YOU :slight_smile:

1 Like