DEV Community

Brad
Brad

Posted on

Stop Running Out of Stock: Python Inventory Tracking That Emails You Automatically

If you've ever had a customer ask for a product you thought you had in stock—only to discover you're completely out—you know the pain.

I run a small e-commerce operation, and for the first two years I tracked inventory in a spreadsheet. I'd forget to check it, run out of things I didn't realize I was running out of, and lose sales.

Then I spent an afternoon writing this Python script. Now I get an email every morning showing me what's low, and another alert the moment anything hits my reorder threshold.

Zero stockouts in the last 8 months.

The Script

import smtplib
import csv
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from datetime import datetime

# Configure these
EMAIL_FROM = "your-email@gmail.com"
EMAIL_TO = "your-email@gmail.com"
EMAIL_PASSWORD = "your-app-password"
REORDER_THRESHOLD = 5  # Alert when quantity drops below this

def load_inventory(filename="inventory.csv"):
    """Load inventory from CSV file."""
    inventory = []
    try:
        with open(filename, "r") as f:
            reader = csv.DictReader(f)
            for row in reader:
                inventory.append({
                    "sku": row["sku"],
                    "name": row["name"],
                    "quantity": int(row["quantity"]),
                    "reorder_point": int(row.get("reorder_point", REORDER_THRESHOLD)),
                    "supplier": row.get("supplier", "Unknown"),
                })
    except FileNotFoundError:
        print(f"Inventory file '{filename}' not found. Creating sample...")
        create_sample_inventory(filename)
        return load_inventory(filename)
    return inventory

def create_sample_inventory(filename):
    """Create a sample inventory CSV."""
    sample = [
        {"sku": "PROD001", "name": "Widget A", "quantity": 50, "reorder_point": 10, "supplier": "Supplier Co"},
        {"sku": "PROD002", "name": "Widget B", "quantity": 3, "reorder_point": 5, "supplier": "Supplier Co"},
        {"sku": "PROD003", "name": "Widget C", "quantity": 0, "reorder_point": 5, "supplier": "Other Supplier"},
    ]
    with open(filename, "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=["sku", "name", "quantity", "reorder_point", "supplier"])
        writer.writeheader()
        writer.writerows(sample)

def check_low_stock(inventory):
    """Return items at or below reorder point."""
    return [item for item in inventory if item["quantity"] <= item["reorder_point"]]

def send_alert_email(low_stock_items):
    """Send an email alert for low stock items."""
    if not low_stock_items:
        print("No low stock items. No email sent.")
        return

    subject = f"🚨 Low Stock Alert - {len(low_stock_items)} item(s) need reordering"

    rows = ""
    for item in low_stock_items:
        status = "OUT OF STOCK" if item["quantity"] == 0 else f"Low ({item['quantity']} left)"
        rows += f"""
        <tr style="{'background: #ffe0e0;' if item['quantity'] == 0 else ''}">
            <td style="padding: 8px; border: 1px solid #ddd;">{item['sku']}</td>
            <td style="padding: 8px; border: 1px solid #ddd;">{item['name']}</td>
            <td style="padding: 8px; border: 1px solid #ddd; font-weight: bold;">{item['quantity']}</td>
            <td style="padding: 8px; border: 1px solid #ddd;">{item['reorder_point']}</td>
            <td style="padding: 8px; border: 1px solid #ddd; color: red; font-weight: bold;">{status}</td>
            <td style="padding: 8px; border: 1px solid #ddd;">{item['supplier']}</td>
        </tr>"""

    html = f"""
    <html><body>
    <h2>⚠️ Inventory Alert - {datetime.now().strftime('%B %d, %Y')}</h2>
    <p>The following items need to be reordered:</p>
    <table style="border-collapse: collapse; width: 100%;">
        <tr style="background: #f0f0f0;">
            <th style="padding: 8px; border: 1px solid #ddd;">SKU</th>
            <th style="padding: 8px; border: 1px solid #ddd;">Name</th>
            <th style="padding: 8px; border: 1px solid #ddd;">In Stock</th>
            <th style="padding: 8px; border: 1px solid #ddd;">Reorder Point</th>
            <th style="padding: 8px; border: 1px solid #ddd;">Status</th>
            <th style="padding: 8px; border: 1px solid #ddd;">Supplier</th>
        </tr>
        {rows}
    </table>
    <br>
    <p>Take action now to avoid stockouts!</p>
    </body></html>
    """

    msg = MIMEMultipart("alternative")
    msg["Subject"] = subject
    msg["From"] = EMAIL_FROM
    msg["To"] = EMAIL_TO
    msg.attach(MIMEText(html, "html"))

    with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
        server.login(EMAIL_FROM, EMAIL_PASSWORD)
        server.sendmail(EMAIL_FROM, EMAIL_TO, msg.as_string())

    print(f"✅ Alert sent for {len(low_stock_items)} item(s)")

def main():
    print(f"🔍 Checking inventory at {datetime.now().strftime('%Y-%m-%d %H:%M')}...")
    inventory = load_inventory()
    print(f"Loaded {len(inventory)} items")

    low_stock = check_low_stock(inventory)
    print(f"Found {len(low_stock)} low/out-of-stock items")

    for item in low_stock:
        print(f"  ⚠️  {item['name']} ({item['sku']}): {item['quantity']} left (reorder at {item['reorder_point']})")

    send_alert_email(low_stock)

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Setting It Up

  1. Save as inventory_alert.py
  2. Create your inventory.csv with columns: sku, name, quantity, reorder_point, supplier
  3. Set up a Gmail App Password (Google Account → Security → App Passwords)
  4. Run it: python inventory_alert.py
  5. Schedule it with cron: 0 8 * * * python /path/to/inventory_alert.py

The Full Picture

This script is part of a collection I built over two years of running my small business. I automated:

  • ✅ Invoice generation and sending
  • ✅ Expense categorization
  • ✅ Inventory monitoring (this script)
  • ✅ Customer follow-up emails
  • ✅ Weekly report generation
  • ✅ Payment reminders

If you want the full toolkit (12 scripts total, ready to run), I packaged everything up at Python Business Automation Toolkit for $29. It includes the code, setup instructions, and my cron schedule templates.

The inventory script alone has saved me from at least 3 stockouts. At $29, it paid for itself the first time it prevented an out-of-stock situation.

Quick Wins From Here

  • Add Slack notifications: Replace the email with a requests.post to a Slack webhook
  • Connect to your e-commerce platform: Shopify has an API, WooCommerce too — swap the CSV load for an API call
  • Track reorder history: Log when you reordered and auto-calculate your average consumption

The 80/20 here: the CSV version works immediately, today, for free. Start there.


What's the most painful manual business task you're still doing? Drop it in the comments — I might have already automated it.

Top comments (0)