- Overview
- Features
- Technical Pros
- Business Logic and User Interaction Flow
- Roles & Permissions
- Getting Started
- API Documentation
- Complaints & Feature Requests
InventoryFlow is a high-performance, asynchronous inventory management API built with Python using the FastAPI framework. It leverages SQLAlchemy for ORM-based database interactions and Alembic for managing database migrations. It supports role-based access, stock tracking, customer/supplier management, and automated email notifications – all wrapped in a sleek, testable REST API.
The system manages inventory by tracking products, stock levels, suppliers, and customers. Incoming and outgoing orders affect the stock accordingly. Admins can monitor stock flow, while customers and suppliers receive relevant email notifications.
Built with scalability, security, and developer experience in mind, InventoryFlow is ideal for small-to-medium businesses, dev demos, or as a base for more complex ERP systems.
- ✅ CRUD operations for Products, Categories, Customers, and Suppliers
- 📦 Track stock, incoming & outgoing orders
- 🔐 Authentication & Authorization with JWT and role-based access
- 📬 Email notifications to suppliers and customers
- 📊 Admin-friendly dashboard endpoints with analytics
- 🔍 **Filtering, and searching for large datasets
- 🔁 Automated unit/integration testing (Pytest)
- 📄 Interactive API docs with Swagger (OpenAPI)
- FastAPI: Provides a modern, high-performance web framework for building the API endpoints.
- SQLAlchemy (Asyncio): Implements an asynchronous Object-Relational Mapper (ORM) for efficient and non-blocking database communication.
- Alembic: Handles database schema migrations, ensuring database state is consistent with application models.
- Pydantic: Enforces strict data validation for request and response models.
- JWT Authentication: Secures endpoints using JSON Web Tokens (JWT) for user authentication and authorization.
- Role-Based Access Control: Implements user roles (e.g., admin, customer) to restrict access to sensitive operations.
- Structured Logging: Utilizes a custom JSON formatter and the Rich library for clear, structured, and easy-to-parse application logs.
InventoryFlow is designed to model real-world inventory processes by defining clear roles and workflows for different user types. The core of the system revolves around the movement of products from suppliers to the warehouse (stock) and then out to customers.
graph LR
subgraph Admin
direction LR
A[Admin]
end
subgraph Staff
direction LR
S[Staff]
end
subgraph Customer
direction LR
C[Customer]
end
subgraph Supplier
direction LR
SU[Supplier]
end
A -- Manages all data & users --> System
S -- Manages inventory & orders --> System
C -- Places orders & views products --> System
SU -- Fulfills incoming orders --> System
System -- Provides comprehensive dashboards --> A
System -- Provides operational data --> S
System -- Shows product catalog & order history --> C
System -- Shows assigned orders & profile --> SU
-
Admin:
- Role: The superuser with complete oversight and control over the entire system.
- Interactions: Admins can manage user accounts (create, update, delete), view system-wide dashboards with analytics, and perform any action available to other roles. They are responsible for system health and high-level management.
-
Staff:
- Role: The primary operators of the inventory system.
- Interactions: Staff members handle the day-to-day logistics. They are responsible for:
- Creating and managing
productsandcategories. - Recording
incoming ordersfrom suppliers, which adds to thestock. - Processing
outgoing ordersfor customers, which deducts from thestock. - Managing
customerandsupplierrecords. - Monitoring inventory levels and operational dashboards.
- Creating and managing
-
Customer:
- Role: The end-consumer who purchases products.
- Interactions: Customers have a limited, focused view of the system. They can:
- Browse the
productcatalog. - Place
outgoing ordersfor products they wish to purchase. - View their own order history and profile.
- Browse the
-
Supplier:
- Role: The entity that provides products to replenish the inventory.
- Interactions: Suppliers interact with the system to manage their supply chain relationship. They can:
- View their own profile information.
- See the
incoming ordersthat have been assigned to them. - Update the status of their assigned orders (e.g., from
pendingtoshipped).
The central logic connects these roles. A Staff member creates an Incoming Order from a Supplier. When this order is fulfilled, a Stock record is created or updated. A Customer then places an Outgoing Order, which consumes from that Stock. The Admin can oversee this entire lifecycle, from supply to sale, through the dashboard.
This model ensures a clear separation of duties and a logical flow of data that mirrors a real-world inventory management process.
| Role | Capabilities |
|---|---|
| Admin | Full access to all endpoints and data |
| Staff | Manage inventory, suppliers, customers, and view dashboards |
| Customer | Limited access: view products, place orders |
| Supplier | View own profile and incoming orders |
- Python 3.10+
- PostgreSQL/MySQL
- SMTP credentials
- UV (optional but recommended)
UV is the recommended way to install and manage InventoryFlow. It's faster and handles dependencies more efficiently.
-
Clone the repository:
git clone https://github.com/NewGenesis04/InventoryFlow.git cd InventoryFlow -
Install dependencies with UV:
uv sync
-
Clone the repository:
git clone https://github.com/NewGenesis04/InventoryFlow.git cd InventoryFlow -
Create a virtual environment:
python -m venv .venv source .venv/bin/activate # On Windows, use `venv\Scripts\activate`
-
Install dependencies:
pip install -r requirements.txt
-
Set up environment variables:
- Copy the example environment file:
cp .env.example .env
- Edit the
.envfile with your database URL and JWT credentials. See Environment Variables.
- Copy the example environment file:
-
Run database migrations:
alembic upgrade head
-
Run the application:
- With UV (No need to enable virtual environment, UV does that by default with "uv run"):
uv run uvicorn app.main:app --reload
- With pip (Ensure virtual environment is enabled first!):
uvicorn app.main:app --reload
The API will be available at
http://localhost:8000. Interactive Swagger API docs would be available athttp://localhost:8000\docs - With UV (No need to enable virtual environment, UV does that by default with "uv run"):
Create a .env file in the root directory and add the following variables:
| Variable | Description | Example |
|---|---|---|
DB_URL |
Your database connection URL | postgresql+asyncpg://user:password@host:port/dbname |
JWT_SECRET_KEY |
Secret key for JWT encoding | your-super-secret-key-that-is-long-and-secure |
JWT_ALGORITHM |
Algorithm for JWT encoding | HS256 |
http://localhost:8000
Description: Checks the API status and database connectivity.
Request: No payload required.
Response:
{
"message": "This is the root endpoint of the InventoryFlow API",
"status": "healthy",
"database": "connected"
}Errors:
500 Internal Server Error: Database connection failed.
Description: Registers a new user.
Request: AuthRegister
{
"username": "johndoe",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"password": "strongpassword123",
"role": "customer",
"phone": "123-456-7890",
"address": "123 Main St"
}Response: User
{
"id": 1,
"username": "johndoe",
"first_name": "John",
"last_name": "Doe",
"role": "customer",
"email": "john.doe@example.com"
}Errors:
400 Bad Request: Email already registered.
Description: Authenticates a user and returns JWT tokens.
Request: AuthLogin
{
"email": "john.doe@example.com",
"password": "strongpassword123"
}Response: AuthResponse
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}Errors:
400 Bad Request: Incorrect email or password.
Description: Retrieves the profile of the currently authenticated user. (Requires authentication)
Response: User
{
"id": 1,
"username": "johndoe",
"first_name": "John",
"last_name": "Doe",
"role": "customer",
"email": "john.doe@example.com"
}Errors:
401 Unauthorized: Invalid token or user not found.
Description: Creates a new product category. (Admin role required)
Request: CategoryCreate
{
"name": "Electronics",
"description": "Gadgets and electronic devices"
}Response: CategoryResponse
{
"id": 1,
"name": "Electronics",
"description": "Gadgets and electronic devices",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:00:00Z"
}Description: Retrieves a list of all product categories. (Admin, Staff, Customer roles required)
Response: List[CategoryResponse]
[
{
"id": 1,
"name": "Electronics",
"description": "Gadgets and electronic devices",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:00:00Z"
}
]Description: Retrieves a single category by its ID. (Admin, Staff, Customer roles required)
Response: CategoryResponse
{
"id": 1,
"name": "Electronics",
"description": "Gadgets and electronic devices",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:00:00Z"
}Description: Updates an existing category. (Admin role required)
Request: CategoryUpdate
{
"name": "Consumer Electronics",
"description": "Updated description for gadgets"
}Response: CategoryResponse
{
"id": 1,
"name": "Consumer Electronics",
"description": "Updated description for gadgets",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:05:00Z"
}Description: Deletes a category by its ID. (Admin role required)
Response: 202 Accepted
Description: Creates a new customer. (Admin role required)
Request: CustomerCreate
{
"user_id": 1,
"first_name": "Jane",
"last_name": "Doe",
"phone_number": "555-555-5555",
"address": "456 Oak Ave"
}Response: CustomerResponse
{
"id": 1,
"user_id": 1,
"first_name": "Jane",
"last_name": "Doe",
"phone_number": "555-555-5555",
"address": "456 Oak Ave",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:00:00Z"
}Description: Retrieves a list of all customers. (Admin, Staff roles required)
Response: List[CustomerSummary]
[
{
"id": 1,
"first_name": "Jane",
"last_name": "Doe"
}
]Description: Retrieves a single customer by ID. (Admin, Staff, Customer roles required)
Response: CustomerResponse
{
"id": 1,
"user_id": 1,
"first_name": "Jane",
"last_name": "Doe",
"phone_number": "555-555-5555",
"address": "456 Oak Ave",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:00:00Z"
}Description: Updates a customer by ID. (Admin role required)
Request: CustomerUpdate
{
"phone_number": "555-555-5556"
}Response: CustomerResponse
{
"id": 1,
"user_id": 1,
"first_name": "Jane",
"last_name": "Doe",
"phone_number": "555-555-5556",
"address": "456 Oak Ave",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:05:00Z"
}Description: Deletes a customer by ID. (Admin role required)
Response: 204 No Content
Description: Retrieves dashboard data. (Admin, Staff roles required)
Response: DashboardResponse
{
"user_overview": {
"total_users": 100,
"new_users_last_30_days": 5,
"user_role_distribution": {
"admin": 2,
"staff": 10,
"customer": 80,
"supplier": 8
}
},
"performance_metrics": {
"inventory": {
"total_products": 500,
"total_categories": 10,
"total_stock_quantity": 10000,
"inventory_value": 500000.00
},
"orders": {
"total_incoming_orders": 50,
"total_outgoing_orders": 200,
"total_incoming_value": 75000.00,
"total_outgoing_value": 120000.00
}
},
"recent_activity": {
"recent_outgoing_orders": [
{
"id": 1,
"customer_id": 1,
"product_id": 1,
"quantity": 2,
"total_price": 2400,
"status": "completed",
"order_date": "2023-10-27T11:00:00Z"
}
],
"recent_user_registrations": [
{
"id": 1,
"username": "johndoe",
"first_name": "John",
"last_name": "Doe",
"role": "customer",
"email": "john.doe@example.com"
}
]
}
}Description: Creates a new product. (Admin role required)
Request: ProductCreate
{
"name": "Laptop Pro",
"description": "A high-end laptop",
"price": 1200,
"category_id": 1
}Response: ProductResponse
{
"id": 1,
"name": "Laptop Pro",
"description": "A high-end laptop",
"price": 1200,
"category": {
"id": 1,
"name": "Electronics"
},
"created_at": "2023-10-27T10:10:00Z",
"updated_at": "2023-10-27T10:10:00Z"
}Description: Retrieves a list of all products. (Admin, Staff, Customer, Supplier roles required)
Response: List[ProductSummary]
[
{
"id": 1,
"name": "Laptop Pro"
}
]Description: Retrieves a single product by its ID. (Admin, Staff, Customer, Supplier roles required)
Response: ProductResponse
{
"id": 1,
"name": "Laptop Pro",
"description": "A high-end laptop",
"price": 1200,
"category": {
"id": 1,
"name": "Electronics"
},
"created_at": "2023-10-27T10:10:00Z",
"updated_at": "2023-10-27T10:10:00Z"
}Description: Updates an existing product. (Admin role required)
Request: ProductUpdate
{
"price": 1150,
"description": "An updated high-end laptop"
}Response: ProductResponse
{
"id": 1,
"name": "Laptop Pro",
"description": "An updated high-end laptop",
"price": 1150,
"category": {
"id": 1,
"name": "Electronics"
},
"created_at": "2023-10-27T10:10:00Z",
"updated_at": "2023-10-27T10:15:00Z"
}Description: Deletes a product by its ID. (Admin role required)
Response: 202 Accepted
Description: Retrieves a list of all stock entries. (Admin, Staff roles required)
Response: List[StockSummary]
[
{
"id": 1,
"available_quantity": 50
}
]Description: Retrieves a specific stock entry by its ID. (Admin, Staff roles required)
Response: StockResponse
{
"id": 1,
"product": {
"id": 1,
"name": "Laptop Pro"
},
"batch_number": "BATCH001",
"available_quantity": 50,
"expiry_date": "2025-12-31T00:00:00Z",
"created_at": "2023-10-27T10:20:00Z",
"updated_at": "2023-10-27T10:20:00Z"
}Description: Manually updates the quantity of a stock entry. (Admin, Staff roles required)
Request: StockUpdate
{
"available_quantity": 45
}Response: StockResponse
{
"id": 1,
"product": {
"id": 1,
"name": "Laptop Pro"
},
"batch_number": "BATCH001",
"available_quantity": 45,
"expiry_date": "2025-12-31T00:00:00Z",
"created_at": "2023-10-27T10:20:00Z",
"updated_at": "2023-10-27T10:25:00Z"
}Description: Creates a new incoming order from a supplier and updates stock. (Admin, Staff roles required)
Request: IncomingOrderCreate
{
"supplier_id": 1,
"product_id": 1,
"batch_number": "BATCH001",
"quantity": 50,
"unit_cost": 800,
"supply_date": "2023-10-27T10:20:00Z",
"expiry_date": "2025-12-31T00:00:00Z"
}Response: IncomingOrderResponse
{
"id": 1,
"supplier": {
"id": 1,
"name": "Supplier A"
},
"product": {
"id": 1,
"name": "Laptop Pro"
},
"batch_number": "BATCH001",
"quantity": 50,
"unit_cost": 800,
"total_cost": 40000,
"status": "pending",
"supply_date": "2023-10-27T10:20:00Z",
"created_at": "2023-10-27T10:20:00Z",
"updated_at": "2023-10-27T10:20:00Z"
}Description: Retrieves a list of all incoming orders. (Admin, Staff roles required)
Response: List[IncomingOrderSummary]
[
{
"id": 1,
"supplier_id": 1,
"product_id": 1,
"batch_number": "BATCH001",
"quantity": 50,
"total_cost": 40000,
"status": "pending",
"supply_date": "2023-10-27T10:20:00Z"
}
]Description: Retrieves a specific incoming order by its ID. (Admin, Staff, Supplier roles required)
Response: IncomingOrderResponse
{
"id": 1,
"supplier": {
"id": 1,
"name": "Supplier A"
},
"product": {
"id": 1,
"name": "Laptop Pro"
},
"batch_number": "BATCH001",
"quantity": 50,
"unit_cost": 800,
"total_cost": 40000,
"status": "pending",
"supply_date": "2023-10-27T10:20:00Z",
"created_at": "2023-10-27T10:20:00Z",
"updated_at": "2023-10-27T10:20:00Z"
}Description: Retrieves all incoming orders for the current supplier. (Supplier role required)
Response: List[IncomingOrderSummary]
[
{
"id": 1,
"supplier_id": 1,
"product_id": 1,
"batch_number": "BATCH001",
"quantity": 50,
"total_cost": 40000,
"status": "pending",
"supply_date": "2023-10-27T10:20:00Z"
}
]Description: Updates the status of an incoming order. (Admin, Staff, Supplier roles required)
Request: IncomingOrderStatusUpdate
{
"status": "completed"
}Response: IncomingOrderResponse
{
"id": 1,
"supplier": {
"id": 1,
"name": "Supplier A"
},
"product": {
"id": 1,
"name": "Laptop Pro"
},
"batch_number": "BATCH001",
"quantity": 50,
"unit_cost": 800,
"total_cost": 40000,
"status": "completed",
"supply_date": "2023-10-27T10:20:00Z",
"created_at": "2023-10-27T10:20:00Z",
"updated_at": "2023-10-27T10:25:00Z"
}Description: Creates a new outgoing order for a customer and decreases stock. (Admin, Staff, Customer roles required)
Request: OutgoingOrderCreate
{
"customer_id": 1,
"product_id": 1,
"stock_id": 1,
"quantity": 2,
"order_date": "2023-10-27T11:00:00Z"
}Response: OutgoingOrderResponse
{
"id": 1,
"customer": {
"id": 1,
"first_name": "Jane",
"last_name": "Doe"
},
"product": {
"id": 1,
"name": "Laptop Pro"
},
"quantity": 2,
"unit_price": 1200,
"total_price": 2400,
"status": "pending",
"order_date": "2023-10-27T11:00:00Z",
"created_at": "2023-10-27T11:00:00Z",
"updated_at": "2023-10-27T11:00:00Z"
}Description: Retrieves a list of all outgoing orders. (Admin, Staff roles required)
Response: List[OutgoingOrderSummary]
[
{
"id": 1,
"customer_id": 1,
"product_id": 1,
"quantity": 2,
"total_price": 2400,
"status": "pending",
"order_date": "2023-10-27T11:00:00Z"
}
]Description: Retrieves a specific outgoing order by its ID. (Admin, Staff, Customer roles required)
Response: OutgoingOrderResponse
{
"id": 1,
"customer": {
"id": 1,
"first_name": "Jane",
"last_name": "Doe"
},
"product": {
"id": 1,
"name": "Laptop Pro"
},
"quantity": 2,
"unit_price": 1200,
"total_price": 2400,
"status": "pending",
"order_date": "2023-10-27T11:00:00Z",
"created_at": "2023-10-27T11:00:00Z",
"updated_at": "2023-10-27T11:00:00Z"
}Description: Creates a new supplier. (Admin role required)
Request: SupplierCreate
{
"user_id": 2,
"name": "Supplier B",
"phone_number": "111-222-3333",
"email": "supplier.b@example.com",
"address": "789 Pine St"
}Response: SupplierResponse
{
"id": 2,
"user_id": 2,
"name": "Supplier B",
"phone_number": "111-222-3333",
"email": "supplier.b@example.com",
"address": "789 Pine St",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:00:00Z"
}Description: Retrieves a list of all suppliers. (Admin, Staff roles required)
Response: List[SupplierSummary]
[
{
"id": 1,
"name": "Supplier A"
},
{
"id": 2,
"name": "Supplier B"
}
]Description: Retrieves the profile of the current supplier. (Supplier role required)
Response: SupplierResponse
{
"id": 1,
"user_id": 1,
"name": "Supplier A",
"phone_number": "123-456-7890",
"email": "supplier.a@example.com",
"address": "123 Maple St",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:00:00Z"
}Description: Retrieves a supplier by ID. (Admin, Staff roles required)
Response: SupplierResponse
{
"id": 1,
"user_id": 1,
"name": "Supplier A",
"phone_number": "123-456-7890",
"email": "supplier.a@example.com",
"address": "123 Maple St",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:00:00Z"
}Description: Updates a supplier by ID. (Admin role required)
Request: SupplierUpdate
{
"phone_number": "123-456-7891"
}Response: SupplierResponse
{
"id": 1,
"user_id": 1,
"name": "Supplier A",
"phone_number": "123-456-7891",
"email": "supplier.a@example.com",
"address": "123 Maple St",
"created_at": "2023-10-27T10:00:00Z",
"updated_at": "2023-10-27T10:05:00Z"
}Description: Deletes a supplier by ID. (Admin role required)
Response: 204 No Content
We welcome feedback, feature requests, and bug reports! To submit a complaint or request a new feature, please open a GitHub issue:
- Navigate to the Issues page
- Click on New Issue
- Use the appropriate tag:
- 🐛
bug- For reporting bugs or problems - ✨
feature- For suggesting new features or improvements - 📝
documentation- For documentation-related issues - ❓
question- For general questions
- 🐛
Please provide as much detail as possible to help us understand and address your request effectively.