Jonnie Grieve Digital Media: Blog

Home
by on 11th October, 2023 - 12:38pm (0)

Blog: Create a Cashflow Forecaster #1 (More Posts)

In this series, I’m going to create another app that mimics something I made on an Excel Spreadsheet. It’s an application that takes a starting figure and calculates changes in the forecast based on inputs and outputs on any given row of data. I’m now using DOM scripting to recreate it.

Like any application, the first thing I did was build the interface; the starting state with HTML and CSS. The cashflow data is stored in a structured way with a <table> element. I made sure that the Cashflow input and balance cells were all editable with input fields and that they blended nicely into the table cells using Sass and CSS.

We also begin with a little JavaScript to retrieve the current date dynamically (according to the UK timezone).

console.log("Cashflow Forecaster - app.js connected - 09-10-2023: 15:28");

/* This section makes DOM selections */

// Get a reference to the "currentDate" element
const currentDateElement = document.getElementById("currentDate");

/* This section is for the Application Functions */

// Function to Display and Update the Date
function updateCurrentDate() {

    const currentDate = new Date(); // Create a new Date object with the current date and time
    const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }; // Date formatting options
    const formattedDate = currentDate.toLocaleDateString('en-UK', options); // Format the date as desired

    // Update the "currentDate" element's text content node
    currentDateElement.textContent = formattedDate;
}

/* This section makes function calls */

// Call the updateCurrentDate function to initialize the date
updateCurrentDate();

Now let’s look a bit closer at the HTML and discuss what the app is supposed to do. This sort of application might be useful in tandem with your bank account. So you can try and predict how your budget is going to look after certain debits and credits that you expect to happen in your account.

You start by matching up the starting balance in the app with your bank balance and then key in your regular banking incomings and outgoings.

The Starting Balance is stored in the element with class:  .js-starting_balance.

<main>

    <div class="starting_balance">

        <h2>Starting Balance (£): <span id="number">
             <!-- £0.00 -->
             <input type="number" value="0.00" class="js-starting_balance" id="" placeholder="" />
            </span>
        </h2>
</div>

Then each table (I may add more groups of forecasts at a later time) is put in a section <section> element of its own with a class of .monthly_table. In the final application, we would be seeing the “Balance” column full of cells full of the number zero or the value that js-starting_balance is set to, which should update dynamically. So if the Starting balance is £5 all the cells in the balance column would also say £5. And this is before any incomings or outgoings for each table row are applied.

<section class="monthly_table">

    <h3 class="cashflow_heading">Month: October</h3>

    <table class="cashflow_table">

    <tbody>

        <tr>
            <th> . . . </th>
            <th> . . . </th>
            . . .
        </tr>

    </tbody>
</table>

The table below should help demonstrate what I mean.

<tr>
    <!--<th>October</th>-->
    <th class="budget_in">In (£)</th>
    <th class="budget_out">Out (£)</th>
    <th class="forecast_date">Date</th>
    <th class="forecast_balance">Balance (£)</th>
    <th class="comments">Comments</th>
    <th class="estimated">Estimated(?) </th>
</tr>
<tr>

    <td><input type="number" class="balance_incoming" id="" placeholder="0.00" value="" min="0" /></td>
    <td><input type="number" class="balance_outgoing" id="" placeholder="0.00" value="" min="0" /></td>
    <td><!--06/10/2023--><input type="date" class="balance_date" id="" value="" /></td>
    <td><!--<input type="number" class="balance_rowcal" id="" placeholder="0.00" value="">-->0.00</td>
    <td><!-- Max Char: 80 -->
    <input type="text" class="forecast_comment" id="" placeholder="Edit comment for each transaction...." title="Enter your comments for each transaction...." value="Rent and housing costs to be paid. This text will overflow. Will overflow." maxlength="80" />
    </td>
    <td class="yes_estimated"></td>
</tr>

Look at the Balance column. In this scenario, if the Starting Balance is as indicated then the entire balance column should show up as £23.55 because that is how the balance should remain if “in and out” have no value.  That goes for the final row of Cashflow totals too.

Now look below the table. For the cashflow totals we now use a set of <div> elements, and we’ve put them under the cashflow table. The first total will be the total sum of the “In” column; the second will be the total sum of the “Out” column with the .total_balances value which will be the remaining balance once all inputs and outputs are taken into account.

        <div class="cashflow_rowtotals">

            <span>Totals: </span>
            <div class="total_incoming">£0.00</div>
            <div class="total_outgoing">£0.00</div>
            <div> </div>
            <div class="total_balance">£0.00</div>
            <div> </div>
            <div> </div>
        </div>

    </section>

</main>

Here’s how it will look when all put together.

Now… that’s the easy part out of the way

Let’s get down to a bit of work.

  • I want to introduce more rows of data.
  • I need the balances column, in the first instance to update all the balance cells to update with the starting balance, assuming there are no incoming or outgoings in each row
  • Inputs and outputs should change the total balances on each row and column,
  • The application should calculate the total number of inputs and outputs.
  • Should calculate the sum of the total inputs and outputs relative to the starting balance.
  • Users should be able to clear the balances and data in the table at any time.

Format the Starting balance

Before we get to all of that in the next post, let’s start with a bit of tidying up. This app is designed to show currency values (in pound sterling) to 2 decimal places. We need to format the starting balance and make sure this value persists; so that it doesn’t change when you refresh or leave the page.

First, we select the element which is a reference to an input form field.

// Get a reference to the starting balance input 
const startingBalanceInput = document.querySelector('.js-starting_balance');

Then, we need to listen for changes in the input box so that we know there’s been a change and to do something with it.

We want to be able to make changes to the value in 2 decimal places. e.g. £75 can be changed to 75.13 So that’s how we will format the values.

What we need to do first is change “input” from “number” so that each time the number increments it reverts to a whole number. We can then edit the value manually.

// Add an event listener to the input for value changes
startingBalanceInput.addEventListener('input', function () { 
    // Get the input value and convert it to a float with two decimal places 
    const inputValue = parseFloat(startingBalanceInput.value).toFixed(2); 
    // Update the input value with the formatted value startingBalanceInput.value = inputValue; 
});

But there’s an issue. It is virtually impossible to control the cursor point when adding the decimal point if we use the function this way. I find that the cursor keeps jumping straight to the end of the input, which is not a good user experience.

Because it was far too picky and not user-friendly. It kept messing with the cursor and not letting me change the tens unit from the single unit of the numbers at will.

So, removing the function restored the UX but we still need to ensure that the Starting balance displays as a floating point number to 2 decimal places. And we need to factor in the data persistence.

/*// Add an event listener to the input for value changes
startingBalanceInput.addEventListener('input', function () {

// Get the input value and convert it to a float with two decimal places
//const inputValue = parseFloat(startingBalanceInput.value).toFixed(2);
//const inputValue = parseFloat(startingBalanceInput.value);

// Update the input value with the formatted value
//startingBalanceInput.value = inputValue;

});*/

Luckily we can do both in one go. Let’s move the number formatting code so that it takes place after the number input takes place. We can do that in a new function.

function saveStartingBalance() {

    const inputValue = parseFloat(startingBalanceInput.value).toFixed(2);
    localStorage.setItem('startingBalance', inputValue);
}

We can take the startingBalanceInput event listener and simply call the function that we just created.

// Add an event listener to the input for value changes
startingBalanceInput.addEventListener('input', function () {

    saveStartingBalance(); // Save the value to localStorage when it changes
});

We can load the saved number to the screen in a function of its own. With a new variable, we store the most recent value that was saved to localhost and assign it as the value of the number input.

// Function to load the starting balance from localStorage
function loadStartingBalance() {

    const savedValue = localStorage.getItem('startingBalance');

    if (savedValue) {
        startingBalanceInput.value = savedValue;
    }
}

// Load the starting balance when the page loads
window.addEventListener('load', loadStartingBalance);

Now we can also increment the number, it doesn’t format it automatically and you can add the decimal places yourself. And it still saves to localhost.

It’s a start. There are quite a few things to get done before this app is complete. I’ll explain how I tackled all of these in later blogs.

This post has been assigned to the following categories

    Leave a Reply

    Your email address will not be published. Required fields are marked *