Cash Register - My code works perfectly but it doesn't pass the tests

Tell us what’s happening:

My code works, but the FCC tests don’t accept it. I believe it’s an error on your side. How can this be fixed? I ran all the tests manually changing the price, cid and cash values and it’s all perfect. I did use the search bar and there are apparently other people who got this issue but unfortunately I do not understand what the solution was. First, I thought it’s because I was working with integers as cents so I adapted it to use floats but STILL it doesn’t pass, while manually it’s all flawless.

This is my JS:

let price = 11.95;
let actualPrice = Math.round(price * 100);
let cid = [
  ["PENNY", 1.01],
  ["NICKEL", 2.05],
  ["DIME", 3.10],
  ["QUARTER", 4.25],
  ["ONE", 90.00],
  ["FIVE", 55.00],
  ["TEN", 20.00],
  ["TWENTY", 80.00],
  ["ONE HUNDRED", 200.00]
];
let actualCid = cid.map(([name, value]) => [name, Math.round(value * 100)]);
console.log(actualCid);
let due = [
  ["PENNY", 0],
  ["NICKEL", 0],
  ["DIME", 0],
  ["QUARTER", 0],
  ["ONE", 0],
  ["FIVE", 0],
  ["TEN", 0],
  ["TWENTY", 0],
  ["ONE HUNDRED", 0]
];
let regStatus = 'open';
let noChange = false;

//get elements
const cashInput = document.getElementById('cash');
const changeDueDiv = document.getElementById('change-due');
const itemPriceSpan = document.getElementById('item-price');
const purchaseBtn = document.getElementById('purchase-btn');
const statusSpan = document.getElementById('status');
const noChangeParagraph = document.getElementById('no-change');
const [...cidSpans] = document.querySelectorAll('.cid-span');
const [...dueSpans] = document.querySelectorAll('.due-span');

//onstart
updateDisplay();

//buttons
purchaseBtn.addEventListener('click', () => {
  purchase();
  updateDisplay();
  changeDueDiv.classList.remove('hidden');
});

//functions
function purchase() {
  const cash = Math.floor(parseFloat(cashInput.value) * 100);
  const change = (cash - actualPrice);
  const totalCid = actualCid.map(el => el[1]).reduce((a, b) => a + b);
  if (regStatus !== 'CLOSED') {
    if (cash >= actualPrice) {
      if (cash > actualPrice) {
        if (totalCid >= change) {
          noChange = false;
          processChange(change);
          updateDisplay();
        } else {
          noChange = false;
          regStatus = 'INSUFFICIENT_FUNDS';
          updateDisplay();
        }
      } else {
        noChange = true;
        updateDisplay();
      }
    } else {
      alert('Customer does not have enough money to purchase the item');
    }
  } else return;
}
  
function processChange(cdue) {
  let work = cdue;
  //100
  const hundreds = calculateAmount(10000, 8, work);
  due[8][1] = hundreds;
  work -= hundreds;
  //20
  const twenties = calculateAmount(2000, 7, work);
  due[7][1] = twenties;
  work -= twenties;
  //10
  const tens = calculateAmount(1000, 6, work);
  due[6][1] = tens;
  work -= tens;
  //5
  const fives = calculateAmount(500, 5, work);
  due[5][1] = fives;
  work -= fives;
  //1
  const ones = calculateAmount(100, 4, work);
  due[4][1] = ones;
  work -= ones;
  //quarters
  const quarters = calculateAmount(25, 3, work);
  due[3][1] = quarters;
  work -= quarters;
  //dimes
  const dimes = calculateAmount(10, 2, work);
  due[2][1] = dimes;
  work -= dimes;
  //nickels
  const nickels = calculateAmount(5, 1, work);
  due[1][1] = nickels;
  work -= nickels;
  //pennies
  const pennies = work;
  due[0][1] = pennies;

  if (actualCid[0][1] >= work) {
    actualCid.forEach((el, index) => {
        el[1] -= due[index][1];
    });
    const totalCid = actualCid.map(el => el[1]).reduce((a, b) => a + b);
    regStatus = totalCid > 0 ? 'OPEN' : 'CLOSED';
  } else {
    regStatus = 'INSUFFICIENT_FUNDS';
    return;
  }
}

function substLimitZero(a, b) {
  return a - b > 0 ? a - b : 0;
}

function calculateAmount(value, cidIndex, working) {
    return (Math.floor(working / value) - substLimitZero(Math.floor(working / value), actualCid[cidIndex][1] / value)) * value;
}

function twoDecimalDisplay(num) {
    return (num / 100).toString();
}

function updateDisplay() {
  itemPriceSpan.textContent = twoDecimalDisplay(actualPrice);
  cidSpans.forEach((span, index) => {
    span.textContent = twoDecimalDisplay(actualCid[index][1]);
  });
  if (!noChange) {
    if (regStatus === 'OPEN' || regStatus === 'CLOSED') {
      noChangeParagraph.classList.add('hidden');
      statusSpan.parentElement.classList.remove('hidden');
      statusSpan.textContent = regStatus;
      dueSpans.forEach((span, index) => {
        span.textContent = twoDecimalDisplay(due[8 - index][1]);

        const dueParagraph = span.parentElement;
        if (due[8 - index][1] <= 0) {
          dueParagraph.classList.add('hidden');
        } else {
          dueParagraph.classList.remove('hidden')
        }
      });
    } else if (regStatus === 'INSUFFICIENT_FUNDS') {
      noChangeParagraph.classList.add('hidden');
      statusSpan.parentElement.classList.remove('hidden');
      statusSpan.textContent = regStatus;
      dueSpans.forEach(span => {
        span.parentElement.classList.add('hidden');
      });
    }
  } else {
    noChangeParagraph.classList.remove('hidden');
    statusSpan.parentElement.classList.add('hidden');
    dueSpans.forEach(span => {
      span.parentElement.classList.add('hidden');
    });
  }
}

This global variable destroys the reusability and thus the functionality of your function

I don’t understand. It just converts the initial cid variable to a more workable format. I can manually change the initial cid variable to whatever values -including the ones the tests give me- and it all works fine. I don’t understand what you mean by reusability,

The thing is I was having the exact same issue I say in the OP before I came up with that. Back then I was just working with integers for cents for the entire thing, and displaying with two decimals only to the user.

Don’t use global variables. The tests change the CID and price variables and your global variables won’t align with the new values.

Essentially, you can only use your code once, which defeats the purpose of a function.

I see. I thought each individual test changed the values and ran the code from zero. My function was perfectly reusable in the sense I could press the button multiple times.

Now this is my only issue. I have no idea why it doesn’t pass, and it’s the only test it still doesn’t pass.

As you can see in the screenshot, it did exactly what’s asked.

Maybe this screenshot is better since the previous one doesn’t show the “highest to lowest order” part.

have you tried to test the failing testcase with some real values so you can compare what you’re getting to what is expected?

Yes that is what I am showing in the screenshot.

can you post the new code here?

Sure.

As you can see you can simulate that last failing test with real values and it works.

you have some extra spaces in your output (at the start just before the word STATUS) and at the end (just after the last thing you print)
Can you try and make them go away?

Edit: there’s something else wrong with the code but I haven’t had time to debug yet. Basically if you try to buy something that leaves the status open, then immediately try to buy something with too much money that leaves the status insufficient funds. THEN try to buy something with exactly the amount of cash needed to get all the remaining change in the drawer, then your code fails to make the status closed. It keeps the status as open.
Try to run multiple consecutive tests to see how you can catch this issue.

Done, but it didn’t work.

I updated my last post.

Sorry I hadn’t refreshed to see your edit. I tried that exact sequence and it worked as intended. Buy and get change with status open, input too much so insufficient funds, input cash to get exact change, status closed and all change in order.

Please post your actual updated code.

Note, your original code still wasn’t ‘perfectly reusable’ as you could not make two purchases back to back.

Edit: just saw the codepen

Thanks yeah it’s the codepen. No you could do many purchases back to back.

Not with the global variables, you could not.

Generally, it’s a bad idea to use global variables that are mutable like that. Functions should use arguments and return values and not have side effects like mutation of global variables. That makes it extremely difficult to trace logical issues.

Yes you could I’m testing the old code with the global variables right now. The page loads once and the initial values in the drawer are not supposed to change so yeah you could do many purchases changing the cash value and pressing the button, but for testing purposes I suppose it’s not good.

Not relevant anymore though, I’m more interested in finding a solution to my current issue.

You aren’t testing the case I’m talking about. Your old approach is broken. Don’t use any global variables other than CID and price.

Yes I am I’m testing my code in the OP of this forum post.

Nope, you aren’t testing the case I’m talking about. Global variable abuse breaks functions.