How to Build a Procurement Dashboard (2024)

Sourcing raw materials and handling incoming stock are some of the most fundamental challenges that logistics teams face. The right procurement dashboard can make all the difference here.

See, a huge part of the problem is visibility. Procurement processes draw on a number of different types of data - across inventories, supply chains, finances, and more.

Without unified, coherent reporting, it’s almost impossible to make sense of all of this.

That’s where procurement dashboards come in.

Today, we’re showing how Budibase can be leveraged to turn data into action - including building custom dashboards around all kinds of existing data sets.

What is a procurement dashboard?

A dashboard is a real-time reporting UI that connects to an external data source. In other words, it’s a preconfigured report that displays the most up-to-date values for whatever data we build it around.

However, this normally isn’t just the raw data itself.

Rather, the goal is to give decision-makers fast, easy access to higher-level KPIs. So, we’ll typically perform transformations and aggregations on our data in order to extract the insights that we need - perhaps even integrating multiple data sets.

In the specific case of a procurement dashboard, we’re most likely to need information about our inventory, orders, shipments, vendors, or other relevant data sets in order to garner the insights our team needs.

So…

What are we building?

Our procurement dashboard is going to consist of two screens - one showing KPIs relating to our purchase orders for the current month - and the other for the current month’s shipments and consignments.

We’ll also drill down into individual vendors and product categories within these.

We’re going to draw our data from four tables within a Postgres database. These represent our vendors, shipments, consignments, and purchase_orders. All of these are interlinked via the shipments table.

We’ll make extensive use of custom queries throughout - but we’ll provide all of the relevant SQL syntax to show what we’re doing as we go along.

Here’s what the purchase orders screen will look like when we’re done:

How to Build a Procurement Dashboard (1)

And the shipments screen:

How to Build a Procurement Dashboard (2)

How to build a procurement dashboard in 8 steps

So, now we know what we’re building, let’s dive right in.

If you haven’t already, sign up for a free Budibase account.

Join 100,000 teams building workflow apps with Budibase

1. Create a Budibase app and connect your data

Our first step is to create a new Budibase application. We can import an existing app or use a template - but we’re starting from scratch. We’ll be prompted to choose a name and URL path for our new app:

How to Build a Procurement Dashboard (3)

Then we need to choose the data we’re going to start with - although we can add more data sources later. Budibase offers dedicated connectors for a range of SQL and NoSQL databases - as well as REST, Google Sheets, and our internal tables:

How to Build a Procurement Dashboard (4)

When we select Postgres, we’re prompted to input our configuration details:

How to Build a Procurement Dashboard (5)

Next, we can choose which tables we want to fetch - essentially pulling them into Budibase so we can use them. Our database has several tables that we don’t need, so we’re just selecting the four that we listed earlier:

How to Build a Procurement Dashboard (6)

Straight off the bat, we can use Budibase’s back-end to perform CRUD operations or alter the schema of any of our tables:

How to Build a Procurement Dashboard (7)

Before we go any further, here’s a quick summary of our data model - specifically, what each table includes and how they all like together.

So, we have:

  • vendors - which stores the vendors’ contact, billing, and product information - including its category.
  • purchase_orders - with an issue_date, complete_date, cost, and po_number.
  • shipments - with a purchase_date, arrive_date, shipment_number, vendor_id, and purchase_order_id.
  • consignments - with an item_name, quantity, consignment_number, and shipment_id.

So, we’ll need to combine information from different tables to get the insights we need - via custom queries.

Let’s jump in.

2. Building summary cards

We’ll start by creating a blank screen, with “/” as its page path. The first component we’ll add is a headline:

How to Build a Procurement Dashboard (8)

We want this to read This Month: followed by the current month in the format MM/YYYY. To do this, hit the lightning bolt icon beside the headline’s text field to open the bindings drawer. Here, we can set a value using plain text, handlebars, or JavaScript:

How to Build a Procurement Dashboard (9)

We’re going to bind this to the following JavaScript expression:

1var currentDate = new Date();23return "This Month: " + (currentDate.getMonth() + 1) + "/" + currentDate.getFullYear();

How to Build a Procurement Dashboard (10)

Beneath this, we’re going to add a container and set its direction to horizontal:

And then we’ll add a cards block inside our container. A cards block is a prebuilt set of components that will iterate over whatever data set we point it at. We can then bind attributes from this data set to display as text:

How to Build a Procurement Dashboard (11)

By the time we’re finished, we’ll have three cards blocks, each displaying a single card.

These will show the following values relating to our purchase_orders table for the current month:

  1. The total cost.
  2. The total number of orders.
  3. The average order value.

To get this information, we’ll need to add a new custom query under our Postgres connection:

How to Build a Procurement Dashboard (12)

We’ll call this PurchaseOrderStatsByMonth.

We’ll use a SELECT statement to return:

  • The numerical month and year extracted from the issue_date attribute.
  • The COUNT of rows.
  • The SUM of the cost attribute.
  • The AVG of the cost attribute.

We’ll also add statements to GROUP and ORDER BY month and year.

So, our query is:

 1SELECT 2 3 CAST(EXTRACT(YEAR FROM issue_date) AS INTEGER) AS year, 4 5 CAST(EXTRACT(MONTH FROM issue_date) AS INTEGER) AS month, 6 7 CAST(COUNT(*) AS INTEGER) AS row_count, 8 9 SUM(cost) AS total_cost,1011 AVG(cost) AS average_cost1213FROM purchase_orders1415GROUP BY year, month1617ORDER BY year, month;

How to Build a Procurement Dashboard (13)

This will return the following data object:

 1{ 2 3 "year": 2023, 4 5 "month": 9, 6 7 "row_count": 3, 8 9 "total_cost": 7125,1011 "average_cost": 23751213}

Head back to the design section. We can now point our cards block to our new query under its data field:

How to Build a Procurement Dashboard (14)

Now we only have two cards, since our sample data only goes back as far as last month:

How to Build a Procurement Dashboard (15)

But, we only want to display a single card, so we need to add a couple of filtering expressions to only display rows where the month and year attribute match the current date:

How to Build a Procurement Dashboard (16)

Again, we’re going to use JavaScript bindings here. So, we’ll bind year to equal:

1var currentDate = new Date();23return currentDate.getFullYear();

And month to:

1var currentDate = new Date();23return currentDate.getMonth() + 1;

JavaScript uses zero-based counting - so the index for January is 0, February is 1 etc. SQL does not. To reflect this, we’ve added one to currentDate.getMonth() in our return statement.

Now we only have one card:

How to Build a Procurement Dashboard (17)

To finish this, we just need to populate the relevant data. We’ll start by renaming our component Total Cost Card.

Then, we’ll bind the following JavaScript expression to bind the title field to the total_cost attribute from our query response - adding a dollar sign and rounding it to two decimal places:

1return "$" + $("Total Cost Card.PurchaseOrderStatsByMonth.total_cost").toFixed(2);

How to Build a Procurement Dashboard (18)

We’ll also give this a descriptive subtitle and remove the description field entirely.

How to Build a Procurement Dashboard (19)

Now, duplicate this card:

How to Build a Procurement Dashboard (20)

We’ll rename this new one and use handlebars to set the title field to the row_count attribute from our response - also updating the subtitle to match:

How to Build a Procurement Dashboard (21)

Two down, one to go.

Duplicate the card again, this time calling the new one Average Cost Card and setting its title binding to the following JavaScript:

1return "$" + $("Average Cost Card.PurchaseOrderStatsByMonth.average_cost").toFixed(2);

How to Build a Procurement Dashboard (22)

And that’s our cards done.

3. Purchase orders by company

Next, we’re going to add two charts, displaying similar statistics about our purchase orders - this time broken up by category.

We want these to appear side-by-side. So we’ll add another horizontal container. We’ll also give this 8px of padding to the top:

How to Build a Procurement Dashboard (23)

We’re going to SELECT the same information as before, but this time we’re going to use JOIN statements to our shipments and vendors tables - so that we can also return and GROUP BY the category attribute which we store about our vendors.

The JOINS will work as follows:

  • The po_number from purchase_orders to the purchase_order_id attribute from shipments.
  • The vendor_id from shipments to the vendor_number from vendors.

Our query is:

 1SELECT 2 3 v.category, 4 5 CAST(EXTRACT(YEAR FROM po.issue_date) AS INTEGER) AS year, 6 7 CAST(EXTRACT(MONTH FROM po.issue_date) AS INTEGER) AS month, 8 9 CAST(COUNT(po.po_number) AS INTEGER) AS row_count,1011 SUM(po.cost) AS total_cost,1213 AVG(po.cost) AS average_cost1415FROM purchase_orders po1617JOIN shipments s ON po.po_number = s.purchase_order_id1819JOIN vendors v ON s.vendor_id = v.vendor_number2021GROUP BY v.category, year, month2223ORDER BY v.category, year, month;

We’ll call this one PurchaseOrderStatsByMonthByCategory:

How to Build a Procurement Dashboard (24)

The response should look like this:

 1{ 2 3 "category": "Biotechnology", 4 5 "year": 2023, 6 7 "month": 10, 8 9 "row_count": 1,1011 "total_cost": 2029,1213 "average_cost": 20291415}

Now, head back to the design section and add a chart block inside our new container.

The chart block has attributes for chart type - to set the type of chart we want - and data to choose a data source. We can then decide which attributes from our data source we want to use for each axis.

Set the data field to our new query and the type to pie. We’ll also set the exact same month and year filters as we did for our cards:

How to Build a Procurement Dashboard (25)

Next, we need to tell it what to display based on our query response. We’ll set the label column (x-axis) to category and the data column (y-axis) to row_count. We’ll also give it a descriptive title and set the width and height to 50% and 400px respectively:

How to Build a Procurement Dashboard (26)

Our second chart will be a bar graph that shows the total and average costs by category. Rather than starting from scratch, we’ll duplicate our existing chart:

How to Build a Procurement Dashboard (27)

This basically saves us from having to configure our filters again. Rename the duplicate and set its type to bar. We’ll keep category as the label column but this time add two data columns - total_cost and average_cost.

We’ll also check the horizontal box:

How to Build a Procurement Dashboard (28)

4. Purchase orders by category

Next, we want to display the same information from our purchase_orders table - but this time broken up by company_name instead of category.

We’ll start by duplicating our existing charts container, but swapping our two charts around so that the bar chart is on the left and the pie chart is on the right:

How to Build a Procurement Dashboard (29)

Next, we’ll need a new query to retrieve the data we want. This will follow the exact same format as before, except that we’ll GROUP the response rows by the company_name attribute from the vendors table - rather than category.

We’ll call this PurchaseOrderStatsByMonthByCompany:

 1SELECT 2 3 v.company_name, 4 5 CAST(EXTRACT(YEAR FROM po.issue_date) AS INTEGER) AS year, 6 7 CAST(EXTRACT(MONTH FROM po.issue_date) AS INTEGER) AS month, 8 9 CAST(COUNT(po.po_number) AS INTEGER) AS row_count,1011 SUM(po.cost) AS total_cost,1213 AVG(po.cost) AS average_cost1415FROM purchase_orders po1617JOIN shipments s ON po.po_number = s.purchase_order_id1819JOIN vendors v ON s.vendor_id = v.vendor_number2021GROUP BY v.company_name, year, month2223ORDER BY v.company_name, year, month;

The response schema is:

 1{ 2 3 "company_name": "Cassin and Sons", 4 5 "year": 2023, 6 7 "month": 9, 8 9 "row_count": 1,1011 "total_cost": 3078,1213 "average_cost": 30781415}

Back in the design section, we can simply swap the data field for our two new charts to this query - and update their label columns to company_name. We’ll also update their names and titles:

How to Build a Procurement Dashboard (30)

Lastly, we’ll change their color palette for better visual separation:

How to Build a Procurement Dashboard (31)

That’s our first screen done for now:

How to Build a Procurement Dashboard (32)

6. Adding a shipments screen

Next, we want to build a very similar UI for data around our shipments table. Start by duplicating this entire screen. We’ll give the new one the page path /shipments.

How to Build a Procurement Dashboard (33)

And we’ll work our way down the screen swapping out the data - starting with our cards. So, on this screen we want four cards, to display this month’s:

  1. Number of shipments.
  2. Average number of consignments per shipment.
  3. Average number of items per shipment.
  4. Average number of items per consignment.

Start by duplicating one of our cards again:

How to Build a Procurement Dashboard (34)

To populate these, we want a new query called ShipmentStatsByMonth. This is a bit more complex, because some of the data we need is stored in shipments and some of it is stored in consignments.

We’ll select:

  • The numerical month and year, extracted from purchase_date in the shipments table.
  • The COUNT of shipments rows.
  • The AVERAGE of the number of consignments rows corresponding to each shipment.
  • The AVERAGE of the number of items within each of these consignments for each shipment.

We’ll use a LEFT JOIN statement to match up shipments rows where the shipment_number matches the shipment_id in the consignments table.

Our query is:

 1SELECT 2 3 CAST(EXTRACT(YEAR FROM s.purchase_date) AS INTEGER) AS year, 4 5 CAST(EXTRACT(MONTH FROM s.purchase_date) AS INTEGER) AS month, 6 7 CAST(COUNT(*) AS INTEGER) AS shipment_count, 8 9 AVG(avg_quantity) AS avg_items_per_shipment,1011AVG(consignment_count) AS avg_consignments_per_shipment,1213 AVG(avg_quantity / consignment_count) AS avg_items_per_consignment1415FROM shipments s1617LEFT JOIN (1819 SELECT2021 shipment_id,2223 COUNT(*) AS consignment_count,2425 SUM(quantity) AS avg_quantity2627 FROM consignments2829 GROUP BY shipment_id3031) c ON s.shipment_number = c.shipment_id3233GROUP BY year, month3435ORDER BY year, month;

And the response:

 1{ 2 3 "year": 2023, 4 5 "month": 9, 6 7 "shipment_count": "3", 8 9 "avg_days_difference": "1.33333333333333333333",1011 "avg_consignments_per_shipment": "3.0000000000000000",1213 "avg_items_per_shipment": 78.66666666666667,1415 "avg_items_per_consignment": 26.2222222222222251617}

Back on the design tab, we’ll use handlebars bindings in the titles to display each of these new response attributes - using the round function where necessary. We’ll also update the subtitle and name attributes to reflect our new data.

That will give us:

How to Build a Procurement Dashboard (35)

7. Shipping stats by company

All four of our charts on this screen will be broken down by company_name. Here’s a summary of what each of these will display:

  • The number of shipments per company as a pie chart.
  • The average items per shipment by company as a bar chart.
  • The average shipping time by company as a bar chart.
  • The average consignments and items per shipment by company as a bar chart.

We’ll need one last custom query to achieve this, called ShipmentStatsByVendor.

This will be quite similar to our previous query, with two additional elements:

  1. We need an additional JOIN statement to SELECT the company_name attribute from the vendors table. We’ll also include this in a GROUP BY statement.
  2. We need to calculate and SELECT the average difference between the arrive_date and the purchase_date from the shipments table - expressed in days.

So, our query is:

 1SELECT 2 3 v.company_name, 4 5 CAST(EXTRACT(YEAR FROM s.purchase_date) AS INTEGER) AS year, 6 7 CAST(EXTRACT(MONTH FROM s.purchase_date) AS INTEGER) AS month, 8 9 CAST(COUNT(s.shipment_number) AS INTEGER) AS shipment_count,1011 AVG(EXTRACT(EPOCH FROM (s.arrive_date - s.purchase_date)) / 86400) AS avg_days_difference,1213 AVG(consignment_count) AS avg_consignments_per_shipment,1415 AVG(avg_quantity) AS avg_items_per_shipment,1617 AVG(avg_quantity / consignment_count) AS avg_items_per_consignment1819FROM shipments s2021LEFT JOIN (2223 SELECT2425 shipment_id,2627 COUNT(*) AS consignment_count,2829 SUM(quantity) AS avg_quantity3031 FROM consignments3233 GROUP BY shipment_id3435) c ON s.shipment_number = c.shipment_id3637LEFT JOIN (3839 SELECT4041 vendor_number,4243 company_name4445 FROM vendors4647) v ON s.vendor_id = v.vendor_number4849GROUP BY v.company_name, year, month5051ORDER BY v.company_name, year, month;

The response should look like this:

 1{ 2 3 "company_name": "Cassin and Sons", 4 5 "year": 2023, 6 7 "month": 10, 8 9 "shipment_count": 1,1011 "avg_days_difference": "4.0000000000000000",1213 "avg_consignments_per_shipment": "3.0000000000000000",1415 "avg_items_per_shipment": 60,1617 "avg_items_per_consignment": 201819}

We’ll alter our first two charts to show the number of shipments per company and the number of items per shipment by company, respectively:

How to Build a Procurement Dashboard (36)

The third chart will show the avg_days_difference attribute - or, our shipping time:

How to Build a Procurement Dashboard (37)

For the fourth and final chart, we’ll change the type to bar and set it to show our avg_consignments_per_shipment and avg_items_per_consignment attributes:

How to Build a Procurement Dashboard (38)

Here’s the completed screen:

How to Build a Procurement Dashboard (39)

8. Design tweaks and publishing

Lastly, let’s make a couple of UX tweaks to our procurement dashboard. We’ll start by hitting configure links under navigation and adding menu items for our two screens:

How to Build a Procurement Dashboard (40)

Then, under screen, we’ll change our app’s theme to darkest:

How to Build a Procurement Dashboard (41)

When you’re ready, you can hit publish to push your procurement dashboard live and send it to users:

How to Build a Procurement Dashboard (42)

Here’s what the finished product looks like:

How to Build a Procurement Dashboard (43)

If you found this tutorial helpful, why not also check out our guide to building a custom vendor request form?

How to Build a Procurement Dashboard (2024)

References

Top Articles
Latest Posts
Article information

Author: The Hon. Margery Christiansen

Last Updated:

Views: 5637

Rating: 5 / 5 (70 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: The Hon. Margery Christiansen

Birthday: 2000-07-07

Address: 5050 Breitenberg Knoll, New Robert, MI 45409

Phone: +2556892639372

Job: Investor Mining Engineer

Hobby: Sketching, Cosplaying, Glassblowing, Genealogy, Crocheting, Archery, Skateboarding

Introduction: My name is The Hon. Margery Christiansen, I am a bright, adorable, precious, inexpensive, gorgeous, comfortable, happy person who loves writing and wants to share my knowledge and understanding with you.