Build a Cash Register Project - Build a Cash Register

Tell us what’s happening:

Please help, I’ve tried everything. The code does what it’s supposed to do, but still won’t test correct. What am I overlooking?

Your code so far

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

/* file: script.js */

/* file: styles.css */

Your browser information:

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

Challenge Information:

Build a Cash Register Project - Build a Cash Register

Hi there!
Add your html, css, and JavaScript files to the topic.

Hello! Thanks for replying. It’s my first time posting here. Thought it added automatically lol. Here it is:

HTML

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

<body>
<div class="mainContainer">
  <div class="centerFlex gap10">
    <h1>Customer</h1>
    <span><label for="cash">Cash </label><input id="cash" placeholder="0"></span>
    <button id="purchase-btn">PURCHASE</button>
  </div>

  <div class="centerFlex">
    <h1>Product</h1>
    <p id="price"></p>
  </div>

  <div class="centerFlex gap10">
    <h1>Register</h1>

    <div class="digitalDisplay">
      <p>— Change Due: <span id="dueTotal"></span> —</p>
      <p id="change-due">Status: </p>
    </div>

    <p>Total in drawer: <span id="totalCash" style="color: gold;"></span></p>
    <div id="cash-in-drawer"></div>
  </div>
</div>

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

</html>

CSS

:root {
	--cd: #161616;
	--cm: #808080;
	--cl: #f2f2f2;
	--ca: #00FFFF;
}

*,
*::before,
*::after {
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}

body {
	font-size: 16px;
  font-family: 'Consolas', sans-serif;
  background-color: #1b1b32;
  color: #fff;
  height: 100vh;
}

#purchase-btn {
  padding: 2%;
  color: #1b1b32;
  background-color: gold;
  border: none;
}

#purchase-btn:active {
  opacity: 0.75;
}

.mainContainer {
  padding: 25px;
  width: 100%;
  display: flex;
  flex-flow: column nowrap;
  gap: 20px;
  background-color: rgb(0,0,0,0.25);
  backdrop-filter: multiply;
}

.centerFlex {
  display: flex;
  flex-flow: column nowrap;
  justify-content: center;
  align-items: center;
  background-color: #1b1b32;
  padding: 5%;
}

.gap50 {
  gap: 50px;
}

.gap25 {
  gap: 25px;
}

.gap10 {
  gap: 10px;
}

.digitalDisplay {
  background-color: black;
  display: block;
  width: 250px;
  min-height: 75px;
  color: #00ff00;
  text-align: right;
  padding: 5px;
}

.digitalDisplay:first-child {
  display: block;
  width: 100%;
}

#cash-in-drawer {
  display: flex;
  flex-flow: column nowrap;
  position: relative;
}

#cash-in-drawer > p {
  flex: 1 0 100%;
  display: flex;
  justify-content: space-between;
  gap: 20px;
}

.coinNum {
  color: #00ffff;
  position: absolute;
  right: -20px;
  text-align: left;
  vertical-align: super;
  font-size: 0.7em;
}

JavaScript

let price = 1.87;
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]
];

const cash = document.getElementById("cash");
const purchaseBtn = document.getElementById("purchase-btn");
const changeDue = document.getElementById("change-due");
const dueTotal = document.getElementById("dueTotal");
  const productPrice = document.getElementById("price");
  const totalCashIn = document.getElementById("totalCash");
  const cashDrawer = document.getElementById("cash-in-drawer");

// ========== PENNYFY VALUES FOR EXACT TRANSACTIONS ==========

const pricePenny = price * 100;
let cashPenny = 0;
let cidPenny = [];

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


const updateCidPenny = () => {
  cidPenny = [...cid.map((slot) => [Math.round(slot[1]*100)])];
  cidPenny.forEach((slot,index) => slot.unshift(cid[index][0]))
  cidPenny.forEach((v,i) => v.push(denominations[i]));
};

updateCidPenny();

// ========== DRAWER TOTAL AMOUNT ==========

const sumArr = (arr) => arr.reduce((t,c) => t + c[1], 0);

let cidTotal = sumArr(cidPenny);

const cointer = () => {
  let arr = [...cidPenny];
  return arr.map((coin, index) => coin[1] / denominations[index])  
};


// ========== CHANGE DUE FUNCTIONS & VALUES ==========

const isChangeDue = (money, price) => {
  return money >= price;
};

const calculateChange = () => {
  return cashPenny - pricePenny;
};

// ==== CHANGE MANAGER
const changeManager = (change) => {
  let stillDue = change;
  let coinHolder = []; /* PRINT PURPOSES */
  let currentDrawer = sumArr(cidPenny);
  let cancelTransaction = false;

  if(change > cidTotal) {
    return "INSUFFICIENT_FUNDS";
  } else {

    let retrieveCoins = [...cidPenny].reverse();
    let fromNextTier = currentDrawer;

    // ===== CYCLE BEGINS
    retrieveCoins.forEach((coin,index) => {
      if(cancelTransaction) {
        console.log("*************CANCELED**********************")
        return;
      }

      
      fromNextTier -= coin[1];
      
      if(stillDue > 0 && stillDue >= coin[2]) {
		let neededFromCoin = stillDue - (stillDue % coin[2]);
		let taken = neededFromCoin > coin[1] ? coin[1] : neededFromCoin;
        coinHolder.unshift([taken]);
        coinHolder[0].unshift(coin[0]);
        stillDue -= taken;
        cidPenny[Math.abs(index - 8)][1] -= taken;
        cid[Math.abs(index - 8)][1] -= taken/100;
	  } else {
        if(stillDue > fromNextTier) {
          cancelTransaction = true;
        }
      }
      
    });
    // ===== CYCLE ENDS
  }

  if(cancelTransaction) {
    return "INSUFFICIENT_FUNDS";
  }
  
  const prePrint = coinHolder.reverse().map((coin) => {
    if(coin[1] == 0) {
      return ""
    }
    return `${coin[0]}: \$${coin[1]/100}`;
  });
  
  return prePrint.join(" ");

};


// ========== UPDATE & DRAW VALUES IN HTML ==========

// Proper Case method from https://stackoverflow.com/a/5574446
String.prototype.toProperCase = function () {
    return this.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
    });
};

// Update values from drawer first
const updateDrawer = () => {
  cashDrawer.innerHTML = "";
  cid.forEach((coin,index) => {
    cashDrawer.innerHTML += `<p><span class="coinTag">${coin[0].toProperCase()}</span> \$${coin[1].toFixed(2)} <span class="coinNum">${cointer()[index]}</span></p>`;
  })
  
  totalCashIn.innerText = "$" + cidTotal/100;
}

const updateAll = () => {
  productPrice.textContent = `\$${price}`;
  updateDrawer();
  updateCidPenny();
  cidTotal = sumArr(cidPenny);
}


// ========== INTERACTIONS ==========

updateAll();

purchaseBtn.addEventListener("click", (e) => {
  e.preventDefault();
  cashPenny = (cash.value) * 100;
  changeDue.textContent = "Status: "

  if(isChangeDue(cashPenny, pricePenny)) {
    let due = calculateChange();
    dueTotal.textContent = due/100;
    switch(due) {
      case 0:
        changeDue.textContent = "No change due - customer paid with exact cash";
        break;
      default:
        const screenPrint = changeManager(due);
        const drawerStatus = () => {
  return cidTotal > 0 ?  "OPEN" : "CLOSED";
}
        updateAll();
        changeDue.textContent += `${drawerStatus()} ${screenPrint}`;
    }
  } else {
    alert("Customer does not have enough money to purchase the item")
  }
  updateAll();
  
  });

Thank you!

You should not have any other global variable except price and cid, outside of the functions.

Thank you! I think I know what you mean. I’ll be back with my new attempt soon :slight_smile:

1 Like

Hi there!

I started from scratch and used OOP this time. Most of the tests have passed except this one:

  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"

This time I’m clueless as to what could be causing the test not to pass, as when I test it, it works.

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

<body>
<div class="mainContainer">
  <div class="centerFlex gap10">
    <h1>Customer</h1>
    <span><label for="cash">Cash </label><input id="cash" placeholder="0"></span>
    <button id="purchase-btn" onClick="updateDrawer(0)">PURCHASE</button>
  </div>

  <div class="centerFlex">
    <h1>Product</h1>
    <p id="price"></p>
  </div>

  <div class="centerFlex gap10">
    <h1>Register</h1>

    <div class="digitalDisplay">
      <p>— Change Due: <span id="dueTotal"></span> —</p>
      <p id="change-due">Status: </p>
    </div>

    <p>Total in drawer: <span id="totalCash" style="color: gold;"></span></p>
    <div id="cash-in-drawer"></div>
  </div>
</div>

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

</html>
CSS
:root {
	--cd: #161616;
	--cm: #808080;
	--cl: #f2f2f2;
	--ca: #00FFFF;
}

*,
*::before,
*::after {
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}

body {
	font-size: 16px;
  font-family: 'Consolas', sans-serif;
  background-color: #1b1b32;
  color: #fff;
  height: 100vh;
}

#purchase-btn {
  padding: 2%;
  color: #1b1b32;
  background-color: gold;
  border: none;
}

#purchase-btn:active {
  opacity: 0.75;
}

.mainContainer {
  padding: 25px;
  width: 100%;
  display: flex;
  flex-flow: column nowrap;
  gap: 20px;
  background-color: rgb(0,0,0,0.25);
  backdrop-filter: multiply;
}

.centerFlex {
  display: flex;
  flex-flow: column nowrap;
  justify-content: center;
  align-items: center;
  background-color: #1b1b32;
  padding: 5%;
}

.gap50 {
  gap: 50px;
}

.gap25 {
  gap: 25px;
}

.gap10 {
  gap: 10px;
}

.digitalDisplay {
  background-color: black;
  display: block;
  width: 250px;
  min-height: 75px;
  color: #00ff00;
  text-align: right;
  padding: 5px;
}

.digitalDisplay:first-child {
  display: block;
  width: 100%;
}

#cash-in-drawer {
  display: flex;
  flex-flow: column nowrap;
  position: relative;
}

#cash-in-drawer > p {
  flex: 1 0 100%;
  display: flex;
  justify-content: space-between;
  gap: 20px;
}

.coinNum {
  color: #00ffff;
  position: absolute;
  right: -20px;
  text-align: left;
  vertical-align: super;
  font-size: 0.7em;
}
JavaScript
let price = 2;
let cid = [
  ['PENNY', 0],
  ['NICKEL', 0],
  ['DIME', 0],
  ['QUARTER', 0],
  ['ONE', 0],
  ['FIVE', 0],
  ['TEN', 20],
  ['TWENTY', 60],
  ['ONE HUNDRED', 100]
];

let cash = document.getElementById("cash");

class Register {
  constructor(pay,pri,draw) {
    this.payment = pay.value * 100;
    this.coins = [...draw.map((coin) => [Math.round(coin[1]*100)])];
    this.coins.forEach((coin,index) => {coin.unshift(draw[index][0])})
    this.denominations = [1,5,10,25,100,500,1000,2000,10000];
    this.total = this.coins.reduce((t,c)=>t+c[1],0);
    this.price = pri * 100;
  }

  status(x) {
    //console.log("> evaluated value: ",x*100)
    if(x <= 0) {
      return "CLOSED"
    } else {
      
      return "OPEN"
    }
  }

  cointer() {
    return this.coins.map((coin,index) => coin[1] / this.denominations[index])
  }

  due() {
    return this.payment - this.price;
  }

  

  count(due) {
    let stillDue = due;
    const dens = [...this.denominations].reverse();
    const available = [...this.coins.map(coin => coin[1])].reverse();
    let availableTotal = available.reduce((t,c)=>t+c,0);
    let holder = [];
    let hand = [];

    console.log("=======================\n",available)
    console.log(availableTotal)

       
    // VERIFICATION
    let pass = available.map((coin,index) => {
      
      let canTake = true;
      let canTakeNext = true;
      
      //oooooooooo
      if(dens[index] > stillDue) {
        canTake = false;
        console.log("total disponible: ",availableTotal)
        if((availableTotal - coin) < stillDue) {
          console.log("exec")
          
          canTakeNext = false;
        }
        availableTotal -= coin;
        holder.unshift([coin]);
        hand.unshift([0]);
      } else {
        if(stillDue > coin) {
          availableTotal -= coin;
          stillDue -=coin;
          holder.unshift([0]);
          hand.unshift([coin]);
          if((availableTotal - coin) < stillDue) {
          console.log("exec")
          
          canTakeNext = false;
        }
          
        }else {
          let calcTake = (stillDue - (stillDue % dens[index]));
          let toTake = calcTake - (calcTake % dens[index]);
          availableTotal -= toTake;
          stillDue -= toTake;
          holder.unshift([coin - toTake]);
          hand.unshift(toTake);
          if((availableTotal - coin) < stillDue) {
          console.log("exec")
          
          canTakeNext = false;
        }
          }
      };
      //oooooooooo

      console.log(`result round #${index}: ${canTake} vs ${canTakeNext}`)
      return canTake || canTakeNext;
    })
    // VERIFICATION ends
    console.log("does it pass? ",pass.every(c=>c))
    return [pass.every(c=>c),holder,hand];
  }

  cancel(due) {
    if(due > this.total) {
      return true;
    } else if(!this.count(due)[0]){
      return true;
    }
    return false;    
  }

  takeChange() {
    let change = this.count(this.due())[1].map(coin => [coin / 100]);
    let hand = this.count(this.due())[2].map(coin => [coin / 100]);
    
    let funds = [...cid.map(dens => dens[0])];
    change.forEach((coin,index) => {coin.unshift(funds[index])});
    hand.forEach((coin,index) => {coin.unshift(funds[index])});
    
    cid = change;
    let changeText = hand.map((coin)=>{
      if(coin[1] > 0) {
        return `${coin[0]}: \$${coin[1]}`;
      } else {
        return ""
      }
      });
      changeText.reverse();
    
    return [change, changeText.join(" ")]
  }

  refresh(draw){
    this.coins = [...draw.map((coin) => [Math.round(coin[1]*100)])];
    this.coins.forEach((coin,index) => {coin.unshift(draw[index][0])})
    this.total = this.coins.reduce((t,c)=>t+c[1],0);
  }
  
  write() {
    const dueScreen = document.getElementById("change-due");
    dueScreen.textContent = ` ${this.takeChange()[1]}`;
    this.refresh(this.takeChange()[0]);
    dueScreen.insertAdjacentText("afterbegin",`Status: ${this.status(this.coins.reduce((t,c)=>t+c[1],0))}`)
    
    this.cointer();
  }

  
}

const updateDrawer = (add) => {
  let initial = 0 + add;
  if(initial >= 0) {
    if(!cash.value || cash.value < price) {
      alert("Customer does not have enough money to purchase the item");
      return
    } else if(cash.value == price) {
      document.getElementById("change-due").textContent = "No change due - customer paid with exact cash";
      return
    }
  }
  const register = new Register(cash, price, cid);

  document.getElementById("price").textContent = `\$${price}`;

  document.getElementById("dueTotal").textContent = `\$${register.due()/100}`;

  if(register.cancel(register.due())) {
    document.getElementById("change-due").textContent = "Status: INSUFFICIENT_FUNDS";
    return
  }
  register.write();

 
  document.getElementById("cash-in-drawer").innerHTML = cid.map((coin,index) => {
    return `<p><span class="coinTag">${coin[0]}</span> \$${coin[1].toFixed(2)}<span class="coinNum">${register.cointer()[index]}</span></p>`}).join(" ");

 document.getElementById("totalCash").textContent = `\$${register.total /100}`;
  
};

updateDrawer(-1);

Thanks!

Thank you for all the help! I finally solved it. I was missing a couple of conditions where the tests failed. I learned how to debug properly! So this was hard for me but completely worth it!

1 Like