Build a Cash Register Project - Build a Cash Register

Tell us what’s happening:

I can’t pass the step 18 and 19. I can’t really figure out how to fix it. I need a helping hand on this. Thank you! :slight_smile:

Your code so far

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

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Digital Cash Register</title>
    <link rel="stylesheet" href="./styles.css">
  </head>
  <body>
    <div class="customer-input-ctn">
      <label for="cash">Enter your cash here:</label>
      <input type="number" id="cash" min="0">
      <button id="purchase-btn">Purchase</button>
    </div>
    
    <div class="cash-register">
      <div class="cash-register-screen">
        <div class="price-indicator">Total: <span id="total-price">$0.00</span></div>
        <div class="change-indicator">Change: <span id="change-due">$0.00</span></div>
      </div>

      <div id="product-list" class="product-list">
        <div class="product-wrapper">
          <p class="product-name">Water Bottle <span class="price-tag">$1.00</span></p>
          <div class="quantity-controls">
            <button class="minus-btn">-</button>
            <p class="product-quantity">0</p>
            <button class="plus-btn">+</button>
          </div>
        </div>
        <div class="product-wrapper">
          <p class="product-name">Bread <span class="price-tag">$2.50</span></p>
          <div class="quantity-controls">
            <button class="minus-btn">-</button>
            <p class="product-quantity">0</p>
            <button class="plus-btn">+</button>
          </div>
        </div>
        <div class="product-wrapper">
          <p class="product-name">Coffee <span class="price-tag">$1.50</span></p>
          <div class="quantity-controls">
            <button class="minus-btn">-</button>
            <p class="product-quantity">0</p>
            <button class="plus-btn">+</button>
          </div>
        </div>
      </div>

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

    <script src="./script.js"></script>
  </body>
</html>

/* file: script.js */

const cash = document.getElementById('cash');
const purchaseBtn = document.getElementById('purchase-btn');
const totalPriceIndicator = document.getElementById('total-price');
const changeDue = document.getElementById('change-due');
const productList = document.getElementById('product-list');
const productQuantity = document.querySelectorAll('.product-quantity');
const changeDrawer = document.getElementById('change-drawer');

let totalChangeToReturn = 0;
let price = 0;
let cart = {};
let cid = [
  ['PENNY', 1.01],        // 101 pennies
  ['NICKEL', 2.05],       // 41 nickels
  ['DIME', 3.1],          // 31 dimes
  ['QUARTER', 4.25],      // 17 quarters
  ['ONE', 90],            // 90 one-dollar bills
  ['FIVE', 55],           // 11 five-dollar bills
  ['TEN', 20],            // 2 ten-dollar bills
  ['TWENTY', 60],         // 3 twenty-dollar bills
  ['ONE HUNDRED', 100]    // 1 hundred-dollar bill
];
const currency = {
  'PENNY': 0.01,
  'NICKEL': 0.05,
  'DIME': 0.10,
  'QUARTER': 0.25,
  'ONE': 1,
  'FIVE': 5,
  'TEN': 10,
  'TWENTY': 20,
  'ONE HUNDRED': 100
}
const productsPrice = {
  'water bottle': 1,
  'bread': 2.5,
  'coffee': 1.5
}

// ===== CALCULATE THE CHANGE =====
function calculateChange(amount) {
  console.log('CID Before:', JSON.stringify(cid));
  let reversedCid = [...cid].reverse();
  let changeToReturn = [];
  let remainingAmount = Math.round(amount * 100);
  let tempCid = [];
  let status = '';
  let totalCashAvailable = Math.round(cid.reduce((sum, [, value]) => sum + value, 0) * 100);
  let cashUsed = 0;
  
  reversedCid.forEach(([cashName, cashAvailable]) => {
    let amountFromThisCurrency = 0;
    let cashAvailableInCents = Math.round(cashAvailable * 100);
    let unitValueInCents = Math.round(currency[cashName] * 100);

    while (remainingAmount >= unitValueInCents && cashAvailableInCents >= unitValueInCents) {
      amountFromThisCurrency += unitValueInCents;
      remainingAmount -= unitValueInCents;
      cashAvailableInCents -= unitValueInCents;
    }

    if (amountFromThisCurrency > 0) {
      changeToReturn.push([cashName, amountFromThisCurrency / 100]);
    }

    cashUsed += amountFromThisCurrency;
    tempCid.push([cashName, cashAvailableInCents / 100]);
  })

  if (remainingAmount > 0) {
    status = 'INSUFFICIENT_FUNDS';
    changeToReturn = [];
  } else if (remainingAmount === 0 && cashUsed === totalCashAvailable) {
    status = 'CLOSED';
    changeToReturn = [...cid];
  } else {
    status = 'OPEN';
  }

  cid = tempCid.reverse();
  updateChangeDue(status, changeToReturn);

  console.log('Total Price:', price);
  console.log('Cash Given:', cash.value);

  console.log('Remaining Amount:', remainingAmount);
  console.log('Change to Return:', changeToReturn);
  console.log('Total Cash Available:', totalCashAvailable);
  console.log('Cash Used:', cashUsed);
  console.log('CID After:', JSON.stringify(tempCid));
}

// ===== CHECK IF CASH IS GREATER THAN THE TOTAL PRICE IN CART =====
function checkCashAmount(cash) {
  if (cash < price || !cash) {
    alert('Customer does not have enough money to purchase the item');
    return;
  } else if (cash === price) {
    changeDue.textContent = 'No change due - customer paid with exact cash';
    return; // remove this soon
  } else if (cash > price) {
    const difference = cash - price;
    calculateChange(difference);
  }
}

// ===== RESET CART OBJECT =====
function resetCart() {
  cart = {};
}

// ===== REMOVE INPUT =====
function removeInput() {
  cash.value = '';
}

// ===== RESET LIST QUANTITY =====
function resetProductListQuantity() {
  productQuantity.forEach((item) => {
    item.textContent = '0';
  })
}

// ===== RESET TOTAL PRICE INDICATOR =====
function resetPriceIndicator() {
  totalPriceIndicator.textContent = '$0.00';
}

// ===== UPDATE CHANGE DUE TEXT CONTENT =====
function updateChangeDue(status, arr) {
  let output = `Status: ${status}<br>`;

  arr.forEach((arr) => {
    const name = arr[0];
    const value = arr[1];

    output += `${name}: $${value}<br>`;
  })

  changeDue.innerHTML = output;
  console.log(cid);
}

// ===== UPDATE THE TOTAL ON PRICE INDICATOR =====
function updatePriceIndicator() {
  totalPriceIndicator.textContent = `$${price.toFixed(2)}`;
}

// ===== CALCULATE THE TOTAL OF PRODUCTS ADDED =====
function calculateCartItems() {
  let temporaryPrice = 0;
  Object.entries(cart).forEach(([product, price]) => {
    temporaryPrice += price * productsPrice[product];
  })
  price = temporaryPrice;
}

// ===== WHEN PLUS BTN OR MINUS BTN IS CLICKED =====
function plusOrMinusBtnIsClicked(event) {
  const plusBtn = event.target.classList.contains('plus-btn');
  const minusBtn = event.target.classList.contains('minus-btn');

  if (plusBtn) { // plus btn is clicked
    const quantity = event.target.previousElementSibling;
    let quantityInt = parseInt(quantity.textContent);
    const productName = event.target.parentElement.previousElementSibling.childNodes[0].textContent.trim().toLowerCase();

    quantityInt += 1;
    quantity.textContent = quantityInt;

    if (cart[productName]) {
      cart[productName] += 1;
    } else {
      cart[productName] = 1;
    }
  }
  
  if (minusBtn) { // minus btn is clicked
    const quantity = event.target.nextElementSibling;
    let quantityInt = parseInt(quantity.textContent);
    const productName = event.target.parentElement.previousElementSibling.childNodes[0].textContent.trim().toLowerCase();

    if (quantityInt > 0) {
      quantityInt -= 1;
      quantity.textContent = quantityInt;
    }

    if (cart[productName]) {
      cart[productName] -= 1;
      if (cart[productName] === 0) {
        delete cart[productName];
      }
    }
  }
}

// ===== LISTEN FOR UPDATE QUANTITY =====
productList.addEventListener('click', (e) => {
  plusOrMinusBtnIsClicked(e);
  calculateCartItems();
  updatePriceIndicator();
})

// ===== LISTEN FOR PURCHASE CLICK =====
purchaseBtn.addEventListener('click', () => {
  const cashValue = parseFloat(cash.value);
  checkCashAmount(cashValue);
  removeInput();
  resetCart();
  resetProductListQuantity()
  resetPriceIndicator()
})

/* file: styles.css */

*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
html {
  font-size: 62.5%;
}
body {
  font-size: 2rem;
}
.cash-register-screen {
  padding: 1rem;
  border: 1px solid black;
}
.product-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1rem;
  border: 1px solid black;
}
.product-wrapper {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  border: 1px solid black;
}
.quantity-controls {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.5rem;
}

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0

Challenge Information:

Build a Cash Register Project - Build a Cash Register

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

okay I will take note about it, but I don’t think that’s the main reason why I failed step 18. I believe it’s somewhere in my calculateChange function. if the issue is really within my global functions, would you please point out what specifically it is? Thank you!

edit:
by the way, I just made some few changes on my javascript file.

JavaScript
const cash = document.getElementById('cash');
const purchaseBtn = document.getElementById('purchase-btn');
const totalPriceIndicator = document.getElementById('total-price');
const changeDue = document.getElementById('change-due');
const productList = document.getElementById('product-list');
const productQuantity = document.querySelectorAll('.product-quantity');
const changeDrawer = document.getElementById('change-drawer');

let price = 0;
let cart = {};
let cid = [
  ['PENNY', 1.01],        // 101 pennies
  ['NICKEL', 2.05],       // 41 nickels
  ['DIME', 3.1],          // 31 dimes
  ['QUARTER', 4.25],      // 17 quarters
  ['ONE', 90],            // 90 one-dollar bills
  ['FIVE', 55],           // 11 five-dollar bills
  ['TEN', 20],            // 2 ten-dollar bills
  ['TWENTY', 60],         // 3 twenty-dollar bills
  ['ONE HUNDRED', 100]    // 1 hundred-dollar bill
];
const currency = {
  'PENNY': 0.01,
  'NICKEL': 0.05,
  'DIME': 0.10,
  'QUARTER': 0.25,
  'ONE': 1,
  'FIVE': 5,
  'TEN': 10,
  'TWENTY': 20,
  'ONE HUNDRED': 100
}
const productsPrice = {
  'water bottle': 1,
  'bread': 2.5,
  'coffee': 1.5
}

// ===== CALCULATE THE CHANGE =====
function calculateChange(amount) {
  console.log('CID Before:', JSON.stringify(cid));
  let localCid = JSON.parse(JSON.stringify(cid));
  let reversedCid = [...localCid].reverse();
  let changeToReturn = [];
  let remainingAmount = Math.round(amount * 100);
  let status = '';
  let totalCashAvailable = Math.round(cid.reduce((sum, [, value]) => sum + value, 0) * 100);
  let cashUsed = 0;
  
  reversedCid.forEach(([cashName, cashAvailable]) => {
    let amountFromThisCurrency = 0;
    let cashAvailableInCents = Math.round(cashAvailable * 100);
    let unitValueInCents = Math.round(currency[cashName] * 100);

    while (remainingAmount >= unitValueInCents && cashAvailableInCents >= unitValueInCents) {
      amountFromThisCurrency += unitValueInCents;
      remainingAmount -= unitValueInCents;
      cashAvailableInCents -= unitValueInCents;
    }

    if (amountFromThisCurrency > 0) {
      changeToReturn.push([cashName, amountFromThisCurrency / 100]);
    }

    cashUsed += amountFromThisCurrency;
    localCid.find(item => item[0] === cashName)[1] = cashAvailableInCents / 100;
  })

  if (remainingAmount > 0) {
    status = 'INSUFFICIENT_FUNDS';
    changeToReturn = [];
  } else if (remainingAmount === 0 && cashUsed === totalCashAvailable) {
    status = 'CLOSED';
    changeToReturn = [...cid];
  } else {
    status = 'OPEN';
  }

  if (status === 'OPEN') {
    cid = localCid;
  }

  updateChangeDue(status, changeToReturn);

  console.log('Total Price:', price);
  console.log('Cash Given:', cash.value);
  console.log('Remaining Amount:', remainingAmount);
  console.log('Change to Return:', changeToReturn);
  console.log('Total Cash Available:', totalCashAvailable);
  console.log('Cash Used:', cashUsed);
  console.log('CID After:', JSON.stringify(localCid));
}

// ===== CHECK IF CASH IS GREATER THAN THE TOTAL PRICE IN CART =====
function checkCashAmount(cash) {
  if (cash < price || !cash) {
    alert('Customer does not have enough money to purchase the item');
    return;
  } else if (cash === price) {
    changeDue.textContent = 'No change due - customer paid with exact cash';
    return; // remove this soon
  } else if (cash > price) {
    const difference = cash - price;
    calculateChange(difference);
  }
}

// ===== RESET CART OBJECT =====
function resetCart() {
  cart = {};
}

// ===== REMOVE INPUT =====
function removeInput() {
  cash.value = '';
}

// ===== RESET LIST QUANTITY =====
function resetProductListQuantity() {
  productQuantity.forEach((item) => {
    item.textContent = '0';
  })
}

// ===== RESET TOTAL PRICE INDICATOR =====
function resetPriceIndicator() {
  totalPriceIndicator.textContent = '$0.00';
}

// ===== UPDATE CHANGE DUE TEXT CONTENT =====
function updateChangeDue(status, arr) {
  let output = `Status: ${status}<br>`;

  arr.forEach((arr) => {
    const name = arr[0];
    const value = arr[1];

    output += `${name}: $${value}<br>`;
  })

  changeDue.innerHTML = output;
  console.log(cid);
}

// ===== UPDATE THE TOTAL ON PRICE INDICATOR =====
function updatePriceIndicator() {
  totalPriceIndicator.textContent = `$${price.toFixed(2)}`;
}

// ===== CALCULATE THE TOTAL OF PRODUCTS ADDED =====
function calculateCartItems() {
  let temporaryPrice = 0;
  Object.entries(cart).forEach(([product, price]) => {
    temporaryPrice += price * productsPrice[product];
  })
  price = temporaryPrice;
}

// ===== WHEN PLUS BTN OR MINUS BTN IS CLICKED =====
function plusOrMinusBtnIsClicked(event) {
  const plusBtn = event.target.classList.contains('plus-btn');
  const minusBtn = event.target.classList.contains('minus-btn');

  if (plusBtn) { // plus btn is clicked
    const quantity = event.target.previousElementSibling;
    let quantityInt = parseInt(quantity.textContent);
    const productName = event.target.parentElement.previousElementSibling.childNodes[0].textContent.trim().toLowerCase();

    quantityInt += 1;
    quantity.textContent = quantityInt;

    if (cart[productName]) {
      cart[productName] += 1;
    } else {
      cart[productName] = 1;
    }
  }
  
  if (minusBtn) { // minus btn is clicked
    const quantity = event.target.nextElementSibling;
    let quantityInt = parseInt(quantity.textContent);
    const productName = event.target.parentElement.previousElementSibling.childNodes[0].textContent.trim().toLowerCase();

    if (quantityInt > 0) {
      quantityInt -= 1;
      quantity.textContent = quantityInt;
    }

    if (cart[productName]) {
      cart[productName] -= 1;
      if (cart[productName] === 0) {
        delete cart[productName];
      }
    }
  }
}

// ===== LISTEN FOR UPDATE QUANTITY =====
productList.addEventListener('click', (e) => {
  plusOrMinusBtnIsClicked(e);
  calculateCartItems();
  updatePriceIndicator();
})

// ===== LISTEN FOR PURCHASE CLICK =====
purchaseBtn.addEventListener('click', () => {
  const cashValue = parseFloat(cash.value);
  checkCashAmount(cashValue);
  removeInput();
  resetCart();
  resetProductListQuantity()
  resetPriceIndicator()
})
const cashValue = parseFloat(cash.value);
  } else if (cash > price) {
    const difference = cash - price;
    calculateChange(difference);
  }

It might be this, I had a similar error. What fixed it was removing decimals before change was calculated. (Including price and payment)

Beautiful code, going to learn from this. :o

1 Like

This line is not in the function.

The tests call the function multiple times, which means this line of code will not run and this value will not be reset between function calls. The second time your function is called totalChangeToReturn will contain the value from the previous call.

The tests specify two variables that should be global:

2. You should have a global variable called price.
3. You should have a global variable called cid.

So you should not have any other global variables other than those two.