Business Problem: Inventory and Procurement Optimization

·

14 min read

Scenario:

A mid-sized retail company, "RetailPro", is facing challenges in managing its inventory and procurement processes efficiently. The company operates multiple stores and warehouses, and it struggles with stockouts, overstocking, and delayed procurement decisions. These inefficiencies lead to increased holding costs, lost sales, and poor customer satisfaction.

Challenges:

  1. Stock Imbalances:

    • Some stores face frequent stockouts, while others have excess inventory.

    • Slow-moving items take up storage space, while fast-moving items run out quickly.

  2. Supplier Performance Issues:

    • Delays in supplier deliveries affect stock availability.

    • Lack of insights into supplier lead times and reliability.

  3. Procurement Inefficiencies:

    • Poor demand forecasting leads to over-ordering or under-ordering.

    • Manual procurement tracking makes it hard to manage purchase orders effectively.

  4. Cost Optimization:

    • High inventory holding costs due to excess stock.

    • Unnecessary procurement expenses due to inefficient order scheduling.


Solution: Power BI Dashboard for Inventory & Procurement Management

Key Insights the Dashboard Provides:

  1. Inventory Overview:

    • Stock levels by product, store, and warehouse.

    • Fast-moving vs. slow-moving inventory.

    • Inventory aging report.

  2. Procurement Insights:

    • Supplier performance analysis (lead times, on-time deliveries).

    • Purchase order tracking and status updates.

    • Procurement spend analysis.

  3. Demand Forecasting & Stock Replenishment:

    • Predictive analytics for stock demand based on past sales trends.

    • Suggested reorder quantities and optimal stock levels.

    • Alerts for low-stock and overstock situations.

  4. Cost & Efficiency Metrics:

    • Inventory turnover ratio.

    • Procurement cost trends.

    • Holding cost vs. stockout cost analysis.


Expected Business Benefits:

Reduced Stockouts & Overstocking: Ensures optimal inventory levels across locations.
Improved Supplier Performance: Identifies reliable suppliers and helps negotiate better terms.
Better Procurement Decisions: Enables data-driven purchasing strategies.
Cost Savings: Lowers storage costs and minimizes wastage.
Enhanced Business Agility: Provides real-time insights for quick decision-making.


Implementation Steps:

  1. Data Integration: Connect Power BI with inventory management, procurement, and sales systems.

  2. Data Modeling: Structure data for better visualization and analysis.

  3. Dashboard Creation: Develop interactive reports for stock levels, supplier performance, and procurement trends.

  4. Deployment & Training: Implement in business operations and train teams to use the insights effectively.

Below is a Python script that generates a dummy Oracle database with relevant tables and populates each table with 1,000 records using numpy for logic-based data creation. The script uses cx_Oracle to connect to an Oracle database.

Prerequisites:

  1. Install required libraries:

     pip install cx_Oracle numpy pandas faker
    
  2. Ensure you have an Oracle database running and update the connection details (USERNAME, PASSWORD, HOST, PORT, SID/ServiceName).

  3. The script creates the following tables:

    • Inventory: Stores stock levels and product details.

    • Suppliers: Contains supplier details and ratings.

    • Procurement: Tracks procurement orders and supplier transactions.

    • Sales: Logs product sales and demand trends.


Python Script for Generating and Inserting Dummy Data

import cx_Oracle
import numpy as np
import pandas as pd
from faker import Faker
import random

# Oracle DB Connection Parameters (Update these)
USERNAME = "your_username"
PASSWORD = "your_password"
HOST = "your_host"
PORT = "your_port"
SERVICE_NAME = "your_service_name"

# Establish Oracle Connection
dsn = cx_Oracle.makedsn(HOST, PORT, service_name=SERVICE_NAME)
conn = cx_Oracle.connect(USERNAME, PASSWORD, dsn)
cursor = conn.cursor()

# Faker Instance for Generating Fake Data
faker = Faker()

# Drop tables if they exist
tables = ["Inventory", "Suppliers", "Procurement", "Sales"]
for table in tables:
    cursor.execute(f"BEGIN EXECUTE IMMEDIATE 'DROP TABLE {table}'; EXCEPTION WHEN OTHERS THEN NULL; END;")

# Create Tables
cursor.execute("""
    CREATE TABLE Inventory (
        Product_ID NUMBER PRIMARY KEY,
        Product_Name VARCHAR2(100),
        Category VARCHAR2(50),
        Stock_Level NUMBER,
        Warehouse_Location VARCHAR2(50),
        Reorder_Threshold NUMBER
    )
""")

cursor.execute("""
    CREATE TABLE Suppliers (
        Supplier_ID NUMBER PRIMARY KEY,
        Supplier_Name VARCHAR2(100),
        Contact VARCHAR2(50),
        Location VARCHAR2(100),
        Rating NUMBER(3,1)
    )
""")

cursor.execute("""
    CREATE TABLE Procurement (
        Order_ID NUMBER PRIMARY KEY,
        Product_ID NUMBER,
        Supplier_ID NUMBER,
        Quantity NUMBER,
        Order_Date DATE,
        Delivery_Date DATE,
        Status VARCHAR2(20),
        FOREIGN KEY (Product_ID) REFERENCES Inventory(Product_ID),
        FOREIGN KEY (Supplier_ID) REFERENCES Suppliers(Supplier_ID)
    )
""")

cursor.execute("""
    CREATE TABLE Sales (
        Sale_ID NUMBER PRIMARY KEY,
        Product_ID NUMBER,
        Quantity_Sold NUMBER,
        Sale_Date DATE,
        Customer_Location VARCHAR2(100),
        FOREIGN KEY (Product_ID) REFERENCES Inventory(Product_ID)
    )
""")

# Generate Dummy Data
categories = ["Electronics", "Furniture", "Clothing", "Groceries", "Toys"]
status_list = ["Pending", "Shipped", "Delivered", "Cancelled"]

# Insert Data into Inventory
inventory_data = []
for i in range(1000):
    inventory_data.append((
        i + 1,
        faker.word().capitalize() + " " + random.choice(["Pro", "Max", "Ultra", "Basic"]),
        random.choice(categories),
        np.random.randint(10, 500),
        faker.city(),
        np.random.randint(5, 100)
    ))

cursor.executemany("INSERT INTO Inventory VALUES (:1, :2, :3, :4, :5, :6)", inventory_data)

# Insert Data into Suppliers
supplier_data = []
for i in range(1000):
    supplier_data.append((
        i + 1,
        faker.company(),
        faker.phone_number(),
        faker.city(),
        round(np.random.uniform(2.5, 5.0), 1)
    ))

cursor.executemany("INSERT INTO Suppliers VALUES (:1, :2, :3, :4, :5)", supplier_data)

# Insert Data into Procurement
procurement_data = []
for i in range(1000):
    product_id = np.random.randint(1, 1001)
    supplier_id = np.random.randint(1, 1001)
    order_date = faker.date_this_year()
    delivery_date = faker.date_between(start_date=order_date, end_date="+30d")

    procurement_data.append((
        i + 1,
        product_id,
        supplier_id,
        np.random.randint(10, 500),
        order_date,
        delivery_date,
        random.choice(status_list)
    ))

cursor.executemany("INSERT INTO Procurement VALUES (:1, :2, :3, :4, :5, :6, :7)", procurement_data)

# Insert Data into Sales
sales_data = []
for i in range(1000):
    product_id = np.random.randint(1, 1001)
    sale_date = faker.date_this_year()

    sales_data.append((
        i + 1,
        product_id,
        np.random.randint(1, 50),
        sale_date,
        faker.city()
    ))

cursor.executemany("INSERT INTO Sales VALUES (:1, :2, :3, :4, :5)", sales_data)

# Commit changes and close connection
conn.commit()
cursor.close()
conn.close()

print("Dummy data successfully inserted into Oracle database!")

Breakdown of the Script:

  1. Connection to Oracle Database

    • Uses cx_Oracle to connect using the provided credentials.
  2. Table Creation

    • Drops existing tables (if they exist) and creates four tables: Inventory, Suppliers, Procurement, and Sales.
  3. Data Generation (Using NumPy & Faker)

    • Uses numpy for generating random numbers logically.

    • Uses faker for generating realistic names, locations, dates, and contact details.

  4. Data Insertion

    • Uses executemany() for bulk insertion (efficient for large datasets).
  5. Finalization

    • Commits the transaction and closes the database connection.

Expected Outcome:

After running the script, your Oracle database will have:

  • Inventory Table: 1,000 records with stock levels and product details.

  • Suppliers Table: 1,000 records of supplier details.

  • Procurement Table: 1,000 records tracking purchase orders.

  • Sales Table: 1,000 records representing product sales.

Database Schema for Inventory & Procurement System

This document provides a detailed schema for the Inventory & Procurement System. It includes four tables: Inventory, Suppliers, Procurement, and Sales, along with column descriptions.


1. Inventory Table

Stores product details and stock levels across warehouses.

Column NameData TypeConstraintsDescription
Product_IDNUMBERPRIMARY KEYUnique identifier for each product.
Product_NameVARCHAR2(100)NOT NULLName of the product.
CategoryVARCHAR2(50)NOT NULLCategory of the product (e.g., Electronics, Furniture).
Stock_LevelNUMBERNOT NULLCurrent stock level for the product.
Warehouse_LocationVARCHAR2(50)NOT NULLWarehouse where the product is stored.
Reorder_ThresholdNUMBERNOT NULLMinimum stock level before reordering is needed.

2. Suppliers Table

Stores information about suppliers providing inventory items.

Column NameData TypeConstraintsDescription
Supplier_IDNUMBERPRIMARY KEYUnique identifier for each supplier.
Supplier_NameVARCHAR2(100)NOT NULLName of the supplier company.
ContactVARCHAR2(50)NOT NULLContact number of the supplier.
LocationVARCHAR2(100)NOT NULLSupplier's location or headquarters.
RatingNUMBER(3,1)CHECK (Rating BETWEEN 0 AND 5)Supplier's performance rating (scale: 0 to 5).

3. Procurement Table

Tracks procurement orders, linking products to suppliers.

Column NameData TypeConstraintsDescription
Order_IDNUMBERPRIMARY KEYUnique identifier for each procurement order.
Product_IDNUMBERFOREIGN KEYInventory(Product_ID)Product being procured.
Supplier_IDNUMBERFOREIGN KEYSuppliers(Supplier_ID)Supplier providing the product.
QuantityNUMBERNOT NULLQuantity of items ordered.
Order_DateDATENOT NULLDate the order was placed.
Delivery_DateDATENULLABLEExpected or actual delivery date.
StatusVARCHAR2(20)CHECK (Status IN ('Pending', 'Shipped', 'Delivered', 'Cancelled'))Order processing status.

4. Sales Table

Records product sales transactions.

Column NameData TypeConstraintsDescription
Sale_IDNUMBERPRIMARY KEYUnique identifier for each sale transaction.
Product_IDNUMBERFOREIGN KEYInventory(Product_ID)Product that was sold.
Quantity_SoldNUMBERNOT NULLNumber of units sold.
Sale_DateDATENOT NULLDate of the sale transaction.
Customer_LocationVARCHAR2(100)NOT NULLLocation of the customer making the purchase.

Relationships Between Tables

  • Inventory(Product_ID)Procurement(Product_ID) (One-to-Many)

    • A product can have multiple procurement orders.
  • Suppliers(Supplier_ID)Procurement(Supplier_ID) (One-to-Many)

    • A supplier can provide multiple products in procurement.
  • Inventory(Product_ID)Sales(Product_ID) (One-to-Many)

    • A product can have multiple sales transactions.

Additional Features for Optimization

Indexes:

  • Create indexes on Product_ID and Supplier_ID for faster joins.

  • Index Order_Date and Sale_Date for efficient date-based queries.

Constraints & Validation:

  • CHECK constraints on Rating (between 0 and 5) and Status (enum values).

  • NOT NULL constraints on essential fields to ensure data integrity.


PowerBI Dashboard Ideas

I'll create a comprehensive Power BI dashboard design document for the RetailPro inventory and procurement management system, including suggested charts, KPIs, and DAX queries.

RetailPro Inventory & Procurement Management Dashboard - Design Document

1. Dashboard Overview

This Power BI solution will address RetailPro's inventory and procurement challenges by providing real-time insights across four key areas:

  1. Inventory Management

  2. Supplier Performance

  3. Procurement Analytics

  4. Sales & Demand Analysis

2. Data Model Structure

Connect to the Oracle database and create the following relationships:

Inventory(Product_ID) ↔ Procurement(Product_ID)
Suppliers(Supplier_ID) ↔ Procurement(Supplier_ID)
Inventory(Product_ID) ↔ Sales(Product_ID)

Calculated Tables

Create a Date dimension table:

Date = CALENDAR(MIN(Sales[Sale_Date]), MAX(Procurement[Delivery_Date]))

3. Key Performance Indicators (KPIs)

Inventory KPIs

  1. Current Stock Value

     Total Stock Value = 
     SUMX(Inventory, Inventory[Stock_Level] * RELATED(Products[Unit_Cost]))
    
  2. Stock Turnover Ratio

     Stock Turnover = 
     DIVIDE(
         CALCULATE(SUM(Sales[Quantity_Sold]), ALL(Date)),
         AVERAGEX(
             VALUES(Date[Month]),
             CALCULATE(SUM(Inventory[Stock_Level]))
         )
     )
    
  3. Stock Below Threshold %

     Stock Below Threshold % = 
     DIVIDE(
         COUNTROWS(FILTER(Inventory, Inventory[Stock_Level] < Inventory[Reorder_Threshold])),
         COUNTROWS(Inventory)
     )
    

Supplier KPIs

  1. Average Supplier Rating

     Avg Supplier Rating = AVERAGE(Suppliers[Rating])
    
  2. On-Time Delivery %

     On-Time Delivery % = 
     DIVIDE(
         COUNTROWS(FILTER(Procurement, Procurement[Delivery_Date] <= Procurement[Order_Date] + 7)),
         COUNTROWS(Procurement)
     )
    
  3. Average Lead Time (Days)

     Avg Lead Time = 
     AVERAGEX(
         Procurement,
         DATEDIFF(Procurement[Order_Date], Procurement[Delivery_Date], DAY)
     )
    

Procurement KPIs

  1. Total Procurement Spend

     Total Procurement Spend = 
     SUMX(
         Procurement,
         Procurement[Quantity] * RELATED(Inventory[Unit_Cost])
     )
    
  2. Open Orders Count

     Open Orders = 
     COUNTROWS(FILTER(Procurement, Procurement[Status] = "Pending" || Procurement[Status] = "Shipped"))
    
  3. Average Order Quantity

     Avg Order Quantity = AVERAGE(Procurement[Quantity])
    

Sales KPIs

  1. Total Sales Quantity

     Total Sales Quantity = SUM(Sales[Quantity_Sold])
    
  2. Sales Growth MoM %

     Sales Growth MoM % = 
     VAR CurrentMonthSales = CALCULATE(SUM(Sales[Quantity_Sold]), DATESMTD(Date[Date]))
     VAR PreviousMonthSales = CALCULATE(SUM(Sales[Quantity_Sold]), DATEADD(DATESMTD(Date[Date]), -1, MONTH))
     RETURN
     DIVIDE(CurrentMonthSales - PreviousMonthSales, PreviousMonthSales)
    
  3. Stock Coverage (Days)

     Stock Coverage = 
     DIVIDE(
         SUM(Inventory[Stock_Level]),
         CALCULATE(
             AVERAGE(Sales[Quantity_Sold]),
             LASTDATE(Date[Date]) - 30
         )
     )
    

4. Dashboard Pages and Visualizations

4.1. Executive Overview Page

Visualizations:

  • Card visuals for top KPIs:

    • Total Stock Value

    • Stock Turnover Ratio

    • Average Supplier Rating

    • Total Procurement Spend

    • Total Sales Quantity

  • Trend line chart: Stock value vs. Sales quantity over time

  • Gauge visual: Stock below threshold %

  • Top/Bottom chart: Top 5 performing suppliers by rating

4.2. Inventory Management Page

Visualizations:

  • Stacked column chart: Stock levels by category and warehouse

  • Heat map: Products by stock level vs. reorder threshold

  • Table: Low stock items (below threshold)

  • Donut chart: Inventory distribution by category

  • Line chart: Stock level trends over time

DAX Queries:

Low Stock Items = 
FILTER(
    SUMMARIZE(
        Inventory,
        Inventory[Product_ID],
        Inventory[Product_Name],
        Inventory[Category],
        Inventory[Stock_Level],
        Inventory[Reorder_Threshold]
    ),
    Inventory[Stock_Level] < Inventory[Reorder_Threshold]
)

4.3. Supplier Performance Page

Visualizations:

  • Scatter plot: Suppliers by rating vs. lead time

  • Column chart: On-time delivery % by supplier (top 10)

  • Card visuals: Avg lead time, On-time delivery %, Avg supplier rating

  • Table: Supplier details with ratings and performance metrics

  • Line chart: Supplier rating trends over time

DAX Queries:

Supplier Performance = 
SUMMARIZE(
    Procurement,
    Suppliers[Supplier_ID],
    Suppliers[Supplier_Name],
    Suppliers[Rating],
    "Lead Time", AVERAGEX(
        RELATEDTABLE(Procurement),
        DATEDIFF(Procurement[Order_Date], Procurement[Delivery_Date], DAY)
    ),
    "On-Time %", DIVIDE(
        COUNTX(
            FILTER(
                RELATEDTABLE(Procurement),
                Procurement[Delivery_Date] <= Procurement[Order_Date] + 7
            ),
            1
        ),
        COUNTX(RELATEDTABLE(Procurement), 1)
    )
)

4.4. Procurement Analytics Page

Visualizations:

  • Stacked bar chart: Orders by status and supplier

  • Line and clustered column chart: Order quantity and spend over time

  • Pie chart: Orders by status

  • Table: Recent orders with status and details

  • Area chart: Cumulative procurement spend

DAX Queries:

Procurement Trends = 
SUMMARIZE(
    Procurement,
    Date[Month],
    "Order Count", COUNTROWS(Procurement),
    "Total Quantity", SUM(Procurement[Quantity]),
    "Total Spend", SUMX(
        Procurement,
        Procurement[Quantity] * RELATED(Inventory[Unit_Cost])
    )
)

4.5. Sales & Demand Analysis Page

Visualizations:

  • Column chart: Sales quantity by product category

  • Line chart: Sales trends over time

  • Map: Sales by customer location

  • Decomposition tree: Analyze sales by hierarchies

  • Forecast chart: Predict future sales based on historical data

DAX Queries:

Sales by Location and Category = 
SUMMARIZE(
    Sales,
    Sales[Customer_Location],
    Inventory[Category],
    "Total Quantity Sold", SUM(Sales[Quantity_Sold]),
    "Sale Count", COUNTROWS(Sales)
)

5. Slicers and Filters

Include the following slicers for interactive filtering:

  • Date range

  • Product category

  • Warehouse location

  • Supplier

  • Order status

6. Advanced Analytics Features

6.1. Forecasting

Set up time series forecasting for:

  • Stock demand based on sales history

  • Supplier delivery times

  • Procurement spend

6.2. Anomaly Detection

Configure anomaly detection to identify:

  • Unusual stock movements

  • Unexpected supplier delays

  • Abnormal sales patterns

7. Dashboard Refresh Schedule

Configure automatic refresh:

  • Main KPIs: Every 4 hours

  • Detailed reports: Daily at midnight

  • Executive summary: Weekly on Monday mornings

8. Implementation Steps

  1. Connect to Oracle database using the provided credentials

  2. Create the data model with proper relationships

  3. Develop calculated measures and tables using the provided DAX queries

  4. Build visualizations as outlined in each dashboard page

  5. Set up slicers and filters for interactive analysis

  6. Configure refresh schedules

  7. Test dashboard with sample scenarios

  8. Deploy to Power BI service for user access

9. User Roles and Permissions

Configure the following access levels:

  • Executives: View-only access to all pages

  • Inventory Managers: Edit access to Inventory Management page

  • Procurement Team: Edit access to Supplier and Procurement pages

  • Sales Team: Edit access to Sales & Demand page

10. Custom Visuals Required

  • KPI cards with trend indicators

  • Inventory heat map

  • Supplier performance scorecard

  • Power KPI custom visual for threshold monitoring

11. Mobile View Configuration

Optimize the Executive Overview page for mobile viewing with:

  • Simplified card visuals

  • Responsive charts

  • Touch-friendly filters


This comprehensive dashboard will provide RetailPro with the insights needed to optimize inventory levels, improve supplier relationships, streamline procurement processes, and align stock with customer demand

More ideas and theory to learn

RetailPro Inventory & Procurement Dashboard: Real-World Benefits

What This Dashboard Does in Simple Terms

This Power BI dashboard takes all the data from RetailPro's systems and turns it into easy-to-understand visuals that help staff make better decisions about inventory and purchasing. Here's what it does and why it matters:

1. Shows What's in Stock and Where

What it does: Shows current inventory levels across all stores and warehouses.

Real-world benefit: Store managers can quickly see when they're running low on popular items so customers don't leave disappointed. It prevents the frustration of one store being out of stock while another has too many of the same item.

2. Highlights Which Suppliers Are Reliable

What it does: Rates suppliers based on on-time deliveries and overall performance.

Real-world benefit: When managers know which suppliers consistently deliver late, they can place orders earlier or find better alternatives. This means fewer empty shelves and happier customers.

3. Tracks Spending on Inventory

What it does: Shows how much money is being spent on ordering products.

Real-world benefit: Helps prevent overspending on inventory that sits unsold. The company saves money that can be used for other needs like hiring staff or improving stores.

4. Predicts What Customers Will Want

What it does: Analyzes past sales to predict future demand.

Real-world benefit: Stores can stock up on items before they're needed. For example, if sales data shows umbrellas always sell out when rainy season starts, managers can order more in advance.

5. Alerts When Stock Is Running Low

What it does: Sends notifications when products drop below minimum levels.

Real-world benefit: Prevents the disappointing "sorry, we're out of stock" conversation with customers. Automatic alerts mean items get reordered before they run out.

How It Helps Different People at RetailPro

For Store Managers:

  • Spend less time checking stock and more time helping customers

  • Know exactly when orders will arrive

  • Make smarter decisions about what to display and promote

For the Procurement Team:

  • Easily see which suppliers deliver on time

  • Know exactly what needs to be ordered and when

  • Reduce paperwork and manual tracking

For Executives:

  • See how the business is performing at a glance

  • Identify problems before they affect sales

  • Make better decisions about where to invest

For Customers (even though they don't see the dashboard):

  • Find the products they want in stock

  • Experience fewer delays and out-of-stock situations

  • Get better service because staff have the information they need

The Bottom Line

This dashboard takes complex data and turns it into actionable insights that help RetailPro:

  • Keep the right products in stock

  • Save money by not over-ordering

  • Work with reliable suppliers

  • Meet customer needs more effectively

Instead of guessing or using outdated information, staff at all levels can make confident decisions based on up-to-date data, ultimately creating a better shopping experience for customers while improving the company's profitability.