Projects • Python

Inventory & Warehouse Management System in Python (Flask + SQLite) with Source Code and Database Design

inventory management system in python featured-techprofree

This is a complete inventory management system in Python that also handles several warehouses: purchases, sales, transfers, internal requisitions, low-stock alerts and a reorder list. It is a web app built with Flask and SQLite, it runs with two commands, and the full source code is free to download below. We wrote and tested every part of it ourselves.

If you need a final-year project you can actually explain in a viva, or a small system you can adapt for a real shop, this is built for that. This page also explains the database design and the one piece of logic most student projects get wrong: stock that never goes negative. Prefer a desktop app? Our older Warehouse Management System in C# is still available.

Inventory management system in Python dashboard showing stock value, sales chart and low stock products

The dashboard: stock value, 14 days of sales and purchases, and what is running low.

Project at a Glance

Item Details
Project TechPro WMS: warehouse and inventory management system
Language Python 3.10 or newer (tested on 3.12)
Framework Flask 3, the only package you install
Database SQLite, created automatically on first run: 13 tables and 1 view
Front end Bootstrap 5.3 and Chart.js 4, both inside the zip, so it works offline
Size One Python file of about 900 lines, 17 page templates, 20 automated tests
Logins admin / admin123 and staff / staff123
Licence MIT: free to use, change and submit as your project

Features of the Inventory Management System

  • Products and categories with SKU, unit, cost price, sale price and a reorder level per product
  • Suppliers, customers and warehouses with search, add, edit and safe delete
  • Purchases (stock in) with several products on one document
  • Sales (stock out) with automatic prices, a running total and a printable invoice
  • Transfers between warehouses that never change the total stock
  • Internal requisitions: staff request items, an admin approves or rejects, stock is issued on approval
  • Stock by warehouse report with value at cost and a low-stock filter
  • Movement ledger: every stock change, with filters and CSV download
  • Reorder list that suggests how much to buy from the last 30 days of sales
  • Two roles (admin and staff), hashed passwords and CSRF protection on every form
  • Database backup download and a read-only JSON API
  • 45 days of demo data loaded on first run, so every screen has something to show
New sale form of the Python inventory management system with two product lines and total

A sale with two product lines. Prices fill in automatically and the total updates as you type.

How to Run the Project (3 Steps)

You need Python installed (get it from python.org; on Windows tick “Add Python to PATH”). New to Python? Start with our Python beginner to advanced notes.

Step 1: Download and extract the zip

The download button is at the end of this page. Extract the zip anywhere, then open a terminal inside the extracted folder.

Step 2: Install Flask

TERMINAL — RUN THISpip install -r requirements.txt

Step 3: Start the app

TERMINAL — RUN THISpython app.py
OUTPUT (REAL — WE RAN IT)Created wms.db with demo data. Login: admin / admin123 or staff / staff123
* Serving Flask app ‘app’
* Debug mode: off
* Running on http://127.0.0.1:5000
Press CTRL+C to quit

Open http://127.0.0.1:5000 in your browser and log in as admin / admin123. The database file wms.db is created for you on the first run.

✏️ Try it: run python app.py --reset any time to start again with fresh demo data.

Inventory Management System Database Design

The database has 13 tables and 1 view. The diagram shows how they connect.

Inventory management system database design ER diagram with 13 tables: products, purchases, sales, stock movements, warehouses

ER diagram of the 13 tables. The yellow table is the stock ledger.

Table What it stores Rows in demo data
users Login accounts, hashed password, role (admin or staff) 2
categories Product groups 4
warehouses Stock locations 2
suppliers, customers Who you buy from and sell to 3 and 4
products SKU, name, unit, prices, reorder level 12
purchases, purchase_items Stock-in documents and their lines 9 and 29
sales, sale_items Stock-out documents and their lines 136 and 256
transfers Moves between two warehouses 1
requisitions Internal requests and who approved them 2
stock_movements The ledger: one row for every stock change 288
v_stock (view) Current stock per product per warehouse 24

The design decision that matters: a stock ledger

Most student projects keep a quantity column on the products table and add or subtract from it. That works until two things update it at once, or one update fails halfway, and then the number is wrong forever with no way to find out why.

This project never stores stock as a number. Every change is a row in stock_movements: +60 for a purchase, -4 for a sale. Current stock is simply the sum of those rows, which this view calculates:

SQL — FROM SCHEMA.SQLCREATE VIEW IF NOT EXISTS v_stock AS
SELECT p.id AS product_id, w.id AS warehouse_id, COALESCE(SUM(m.qty), 0) AS qty
FROM products p
CROSS JOIN warehouses w
LEFT JOIN stock_movements m ON m.product_id = p.id AND m.warehouse_id = w.id
GROUP BY p.id, w.id;
Why this is worth mentioning in your viva: you get a full audit trail for free, and stock can always be explained row by row. It is the same idea accountants use. For another worked schema, see our school management system database design.

Code Walkthrough: A Sale That Cannot Oversell

Two short pieces of app.py do the important work. First, a helper that makes a block of database work all-or-nothing:

PYTHON — FROM APP.PY@contextmanager
def transaction():
“””All-or-nothing block. BEGIN IMMEDIATE takes the write lock first, so two
cashiers cannot both sell the last unit of the same product.”””
db = get_db()
db.execute(“BEGIN IMMEDIATE”)
try:
yield db
db.commit()
except Exception:
db.rollback()
raise

Then the sale itself. It checks every line first and only then writes anything. If one product is short, the whole sale is cancelled, including the lines that were fine:

PYTHON — FROM APP.PYdef post_sale(customer_id, warehouse_id, lines, user_id, note=””):
lines = merge_lines(lines)
with transaction() as db:
for product_id, qty, _ in lines: # check everything first…
require_stock(db, product_id, warehouse_id, qty)
cur = db.execute(
“INSERT INTO sales (customer_id, warehouse_id, total, note, created_by) VALUES (?, ?, ?, ?, ?)”,
(customer_id, warehouse_id, sum(q * p for _, q, p in lines), note, user_id))
sale_id = cur.lastrowid
for product_id, qty, price in lines: # …then write everything
db.execute(“INSERT INTO sale_items (sale_id, product_id, qty, unit_price) VALUES (?, ?, ?, ?)”,
(sale_id, product_id, qty, price))
move(db, product_id, warehouse_id, -qty, “sale”, f”SALE-{sale_id}”, user_id)
return sale_id

Here is what happens when someone tries to sell two power banks while the stock is zero. Nothing is saved:

REAL MESSAGE FROM THE APPNot enough stock for Power Bank 10000mAh: have 0, need 2
Inventory system refusing a sale with the message Not enough stock for Power Bank: have 0, need 2

The sale form after an oversell attempt. The stock and the sales table are unchanged.

Proof It Works: 20 Automated Tests

The zip includes test_app.py. Run it with one command:

TERMINAL — RUN THISpython -m unittest -v
OUTPUT (REAL — SHORTENED TO THE KEY LINES)test_overselling_is_refused_and_nothing_is_saved … ok
test_same_product_on_two_lines_is_checked_together … ok
test_transfer_moves_stock_without_changing_the_total … ok
test_requisition_flow … ok
test_staff_cannot_open_admin_pages … ok
test_post_without_csrf_token_is_blocked … ok
test_backup_is_a_real_sqlite_file … ok
test_stock_never_negative_anywhere … ok
…
———————————————————————-
Ran 20 tests in 12.707s

OK

✏️ Viva tip: if your examiner asks “how do you know it works?”, run this in front of them.

The Reorder List: A Simple Sales Forecast

The reorder page answers “what should I buy today, and how much?” using a formula you can explain in one breath:

  • Daily sales = units sold in the last 30 days, divided by 30
  • Reorder point = the larger of the product’s own reorder level and 10 days of sales (7 days supplier lead time plus 3 safety days)
  • Suggested quantity = 30 days of sales + reorder point – stock on hand
Reorder list in the Python inventory management system showing days of stock left and suggested order quantity

The reorder list: days of stock left and how much to order now.

The same numbers are available as JSON, so a mobile app or another system can read them:

TERMINAL — RUN THIScurl -H “X-API-Key: demo-key” http://127.0.0.1:5000/api/v1/low-stock
OUTPUT (REAL — FROM THE DEMO DATA)[{“id”:3,”name”:”Power Bank 10000mAh”,”on_hand”:0,”reorder_level”:10,”sku”:”EL-003″,”unit”:”pcs”},
{“id”:11,”name”:”Hammer 500g”,”on_hand”:2,”reorder_level”:8,”sku”:”HW-001″,”unit”:”pcs”},
{“id”:12,”name”:”Screwdriver Set”,”on_hand”:3,”reorder_level”:8,”sku”:”HW-002″,”unit”:”set”}]

The API key is switched on by setting the WMS_API_KEY environment variable before you start the app. Without a key or a login the API answers 401.

More Screens

Stock by warehouse report with quantity in Main Warehouse and City Store and value at cost

Stock by warehouse, with value at cost. Admins can correct a count here.

Internal requisitions screen where an admin approves or rejects a stock request

Requisitions: staff ask, an admin approves or rejects.

Printable sales invoice generated by the inventory management system

Every sale gets a printable invoice.

5 Common Errors (With the Exact Messages)

1. No module named ‘flask’

REAL ERROR from flask import (Flask, Response, abort, flash, g, jsonify, redirect,
ModuleNotFoundError: No module named ‘flask’

You skipped step 2. Run pip install -r requirements.txt. If you have several Python versions, use python -m pip install -r requirements.txt so Flask goes into the same Python that runs the app.

2. Port 5000 is in use

REAL ERRORAddress already in use
Port 5000 is in use by another program. Either identify and stop that program, or start the server with a different port.

The app is already running in another terminal, or another program holds the port (on macOS, AirPlay Receiver uses 5000). Close the other copy, or start on another port: on Windows set PORT=5050 then python app.py; on macOS or Linux PORT=5050 python app.py.

3. ‘python’ is not recognized

STANDARD WINDOWS MESSAGE‘python’ is not recognized as an internal or external command,
operable program or batch file.

Python is not on PATH. Reinstall Python and tick “Add Python to PATH”, or use py app.py instead.

4. Error 400: form expired

REAL ERRORError 400
Form expired or CSRF token missing. Go back, refresh the page and try again.

You logged out in another tab, or the form was open for a long time. Go back, refresh and submit again. This check is deliberate: it stops other websites from submitting forms on your behalf.

5. Not enough stock

REAL MESSAGENot enough stock for Power Bank 10000mAh: have 0, need 2

This is the system doing its job. Receive stock with a purchase, transfer it from another warehouse, or (as admin) correct the count on the Stock by warehouse page.

Ideas to Extend It (Future Work for Your Report)

  • Barcode scanning: a USB scanner types the SKU, so a search box on the sale form is enough
  • PDF invoices and email alerts when a product reaches its reorder point
  • Purchase orders created directly from the reorder list
  • PostgreSQL or MySQL for many users at once; the SQL is standard and needs few changes
  • Deploy it free on PythonAnywhere or Render, after changing the default passwords and the WMS_SECRET key
  • Batch and expiry dates for medicines or food, like our pharmacy POS in C#

For Your Project Report

Abstract (edit to suit): This project is a web-based inventory and warehouse management system developed in Python using the Flask framework and an SQLite database. It records purchases, sales, transfers between warehouses and internal requisitions, and derives current stock from a movement ledger so that every change is traceable. Role-based access separates administrators from staff. The system reports low stock and suggests reorder quantities from recent sales, and its core rules are verified by automated tests.
Section What to write
Objectives Keep accurate stock across several warehouses; prevent sales of items that are not in stock; give managers a daily reorder list; keep a complete history of every stock movement
Modules Authentication and users; catalog; purchasing; sales and invoicing; transfers; requisitions; reports (stock, ledger, reorder); backup and API
Tools Python 3, Flask 3, SQLite 3, Bootstrap 5, Chart.js 4, unittest
Testing 20 automated tests covering login, roles, CSRF, stock rules, reports, backup and API

Related Projects

📥 Download the Source Code

TechPro WMS: Inventory & Warehouse Management System in Python

app.py, schema.sql, 17 templates, 20 tests, Bootstrap and Chart.js, README. Our own code, MIT licence, 135 KB, no sign-up.

Download the Source Code (ZIP)

Run it. Break it. Extend it.

Download the zip, run the 20 tests, then add one feature of your own. That is a project you can defend. Get the source code

Frequently Asked Questions

Is this inventory management system in Python free to use for a final-year project?

Yes. The code is released under the MIT licence, so you can use it, change it and submit it. Make it your own: add one of the extensions above, and be ready to explain the stock ledger and the transaction code.

Which database does it use, and do I need to install one?

It uses SQLite, which is built into Python. There is nothing to install or configure. The file wms.db is created the first time you run python app.py.

Can I change the currency from Rs. to my own?

Yes. Open app.py, find CURRENCY="Rs." near the top and change it to your symbol. Every page picks it up.

Does it work without internet?

Yes. After Flask is installed, everything runs on your own computer. Bootstrap and Chart.js are inside the zip instead of being loaded from a CDN.

Can it be used in a real shop or warehouse?

For a small business on one computer or a local network, yes, after you change the default passwords and the WMS_SECRET key and set up regular backups. For many users at the same time, move the database to PostgreSQL or MySQL first.

What is the difference between inventory management and warehouse management?

Inventory management tracks what you have and what it is worth. Warehouse management adds where it is and how it moves: locations, transfers and internal requests. This project covers both, with stock kept separately for each warehouse.