Are you looking to build a real-world Inventory Management System using Laravel 13 and Filament 5? You are at the right place. In this step-by-step tutorial series, we will build a complete, production-ready Inventory Management System from scratch - including product management, stock tracking, purchases, sales, multi-warehouse support, and reports.
This is a free video + source code tutorial series, and this blog post will be updated with source code and explanation as soon as every new part is published on our YouTube channel Webslesson.
👉 Bookmark this page - we will keep adding new parts, source code, and downloadable files here as the series continues.
What You Will Learn in This Series
- How to install Laravel 13 and Filament 5 from scratch
- How to design a real-world database schema for an inventory system
- How to build Filament Resources for products, categories, purchases, sales, and stock
- How to manage multi-warehouse stock tracking
- How to generate reports and dashboards using Filament Widgets
- How to deploy the final project live
Technologies Used
| Technology | Purpose |
|---|---|
| Laravel 13 | Backend framework |
| Filament 5 | Admin panel builder |
| MySQL | Database |
| Livewire 4 | Reactive UI components (used internally by Filament 5) |
Part 1 - Installation & Setup Guide
Video Title: Installation & Setup Guide | Laravel Filament 5 Complete Course 2026 | Beginner to Advanced – Part 1
In the first part of this series, we cover the complete setup process needed before we start building the actual project. Here is everything covered in this part, along with the exact commands used.
Step 1: Create a New Laravel 13 Project
composer create-project laravel/laravel inventory-management
cd inventory-management
Step 2: Install Filament 5
composer require filament/filament:"^5.0"
Step 3: Install the Filament Admin Panel
php artisan filament:install --panels
This command creates a new panel provider file at:
app/Providers/Filament/AdminPanelProvider.php
It also sets up the default panel route, which you can access at /admin once your server is running.
Step 4: Run the Default Laravel Migration
Laravel already ships with a few default migration files (users table, cache table, jobs table). We run these first to set up the base authentication system:
php artisan migrate
Step 5: Create the Admin User
To log in to the Filament admin panel, we need at least one user account:
php artisan make:filament-user
This will ask for a name, email, and password in the terminal, and create your first admin account.
Step 6: Start the Development Server
php artisan serve
Now open your browser and visit:
http://127.0.0.1:8000/admin
Log in using the admin credentials you just created, and you will see the default Filament dashboard - ready for us to start building on top of it.
Part 2 - Database Design
Video Title: Database Design | Laravel Filament 5 Complete Course 2026 | Inventory Management System – Part 2
In part 2, we design the complete database structure for our Inventory Management System. A solid database design is the foundation of the entire project, so we planned this carefully before writing any Filament code.
Database Tables Used in This Project
Our Inventory Management System uses the following 12 tables:
categories- product categories, with parent/child supportunits- measuring units (pcs, kg, box, etc.)suppliers- vendors we purchase stock fromcustomers- people we sell products towarehouses- physical stock locationsproducts- the core product tablepurchases- purchase order recordspurchase_items- products inside each purchase ordersales- sale/invoice recordssale_items- products inside each salestocks- current stock quantity per product per warehousestock_adjustments- manual stock corrections (damage, loss, etc.)
Step 1: Create Migration Files
Run the following commands in order, since some tables depend on others through foreign keys:
php artisan make:migration create_categories_table
php artisan make:migration create_units_table
php artisan make:migration create_suppliers_table
php artisan make:migration create_customers_table
php artisan make:migration create_warehouses_table
php artisan make:migration create_products_table
php artisan make:migration create_purchases_table
php artisan make:migration create_purchase_items_table
php artisan make:migration create_sales_table
php artisan make:migration create_sale_items_table
php artisan make:migration create_stocks_table
php artisan make:migration create_stock_adjustments_table
Each command generates a migration file inside:
database/migrations/
Step 2: Categories Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_categories_table.php
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->foreignId('parent_id')->nullable()->constrained('categories')->nullOnDelete();
$table->boolean('status')->default(true);
$table->timestamps();
});
Step 3: Units Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_units_table.php
Schema::create('units', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('short_code');
$table->timestamps();
});
Step 4: Suppliers Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_suppliers_table.php
Schema::create('suppliers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->nullable();
$table->string('phone')->nullable();
$table->text('address')->nullable();
$table->boolean('status')->default(true);
$table->timestamps();
});
Step 5: Customers Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_customers_table.php
Schema::create('customers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->nullable();
$table->string('phone')->nullable();
$table->text('address')->nullable();
$table->boolean('status')->default(true);
$table->timestamps();
});
Step 6: Warehouses Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_warehouses_table.php
Schema::create('warehouses', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('location')->nullable();
$table->boolean('status')->default(true);
$table->timestamps();
});
Step 7: Products Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_products_table.php
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->string('sku')->unique();
$table->foreignId('category_id')->constrained('categories')->cascadeOnDelete();
$table->foreignId('unit_id')->constrained('units')->cascadeOnDelete();
$table->decimal('cost_price', 12, 2)->default(0);
$table->decimal('selling_price', 12, 2)->default(0);
$table->string('image')->nullable();
$table->text('description')->nullable();
$table->boolean('status')->default(true);
$table->timestamps();
});
Step 8: Purchases Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_purchases_table.php
Schema::create('purchases', function (Blueprint $table) {
$table->id();
$table->foreignId('supplier_id')->constrained('suppliers')->cascadeOnDelete();
$table->foreignId('warehouse_id')->constrained('warehouses')->cascadeOnDelete();
$table->string('invoice_no')->unique();
$table->date('purchase_date');
$table->decimal('total_amount', 12, 2)->default(0);
$table->decimal('paid_amount', 12, 2)->default(0);
$table->decimal('due_amount', 12, 2)->default(0);
$table->string('status')->default('pending');
$table->timestamps();
});
Step 9: Purchase Items Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_purchase_items_table.php
Schema::create('purchase_items', function (Blueprint $table) {
$table->id();
$table->foreignId('purchase_id')->constrained('purchases')->cascadeOnDelete();
$table->foreignId('product_id')->constrained('products')->cascadeOnDelete();
$table->integer('quantity');
$table->decimal('unit_cost', 12, 2);
$table->decimal('subtotal', 12, 2);
$table->timestamps();
});
Step 10: Sales Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_sales_table.php
Schema::create('sales', function (Blueprint $table) {
$table->id();
$table->foreignId('customer_id')->constrained('customers')->cascadeOnDelete();
$table->foreignId('warehouse_id')->constrained('warehouses')->cascadeOnDelete();
$table->string('invoice_no')->unique();
$table->date('sale_date');
$table->decimal('total_amount', 12, 2)->default(0);
$table->decimal('paid_amount', 12, 2)->default(0);
$table->decimal('due_amount', 12, 2)->default(0);
$table->string('status')->default('pending');
$table->timestamps();
});
Step 11: Sale Items Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_sale_items_table.php
Schema::create('sale_items', function (Blueprint $table) {
$table->id();
$table->foreignId('sale_id')->constrained('sales')->cascadeOnDelete();
$table->foreignId('product_id')->constrained('products')->cascadeOnDelete();
$table->integer('quantity');
$table->decimal('unit_price', 12, 2);
$table->decimal('subtotal', 12, 2);
$table->timestamps();
});
Step 12: Stocks Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_stocks_table.php
Schema::create('stocks', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id')->constrained('products')->cascadeOnDelete();
$table->foreignId('warehouse_id')->constrained('warehouses')->cascadeOnDelete();
$table->integer('quantity')->default(0);
$table->timestamps();
$table->unique(['product_id', 'warehouse_id']);
});
Step 13: Stock Adjustments Table
File path: database/migrations/xxxx_xx_xx_xxxxxx_create_stock_adjustments_table.php
Schema::create('stock_adjustments', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id')->constrained('products')->cascadeOnDelete();
$table->foreignId('warehouse_id')->constrained('warehouses')->cascadeOnDelete();
$table->enum('type', ['add', 'subtract']);
$table->integer('quantity');
$table->string('reason')->nullable();
$table->foreignId('adjusted_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();
});
Step 14: Run the Migration
Once all migration files are ready, run:
php artisan migrate
This creates all 12 tables in your database in the correct order.
Step 15: Create Eloquent Models
php artisan make:model Category
php artisan make:model Unit
php artisan make:model Supplier
php artisan make:model Customer
php artisan make:model Warehouse
php artisan make:model Product
php artisan make:model Purchase
php artisan make:model PurchaseItem
php artisan make:model Sale
php artisan make:model SaleItem
php artisan make:model Stock
php artisan make:model StockAdjustmentEach model is generated inside:
app/Models/
Category Model (app/Models/Category.php):
class Category extends Model
{
protected $fillable = ['name', 'slug', 'parent_id', 'status'];
protected $casts = ['status' => 'boolean'];
public function parent()
{
return $this->belongsTo(Category::class, 'parent_id');
}
public function children()
{
return $this->hasMany(Category::class, 'parent_id');
}
public function products()
{
return $this->hasMany(Product::class);
}
}
Unit Model (app/Models/Unit.php):
class Unit extends Model
{
protected $fillable = ['name', 'short_code'];
public function products()
{
return $this->hasMany(Product::class);
}
}
Supplier Model (app/Models/Supplier.php):
class Supplier extends Model
{
protected $fillable = ['name', 'email', 'phone', 'address', 'status'];
protected $casts = ['status' => 'boolean'];
public function purchases()
{
return $this->hasMany(Purchase::class);
}
}
Customer Model (app/Models/Customer.php):
class Customer extends Model
{
protected $fillable = ['name', 'email', 'phone', 'address', 'status'];
protected $casts = ['status' => 'boolean'];
public function sales()
{
return $this->hasMany(Sale::class);
}
}
Warehouse Model (app/Models/Warehouse.php):
class Warehouse extends Model
{
protected $fillable = ['name', 'location', 'status'];
protected $casts = ['status' => 'boolean'];
public function stocks()
{
return $this->hasMany(Stock::class);
}
public function purchases()
{
return $this->hasMany(Purchase::class);
}
public function sales()
{
return $this->hasMany(Sale::class);
}
}
Product Model (app/Models/Product.php):
class Product extends Model
{
protected $fillable = [
'name', 'slug', 'sku', 'category_id', 'unit_id',
'cost_price', 'selling_price', 'image', 'description', 'status',
];
protected $casts = [
'status' => 'boolean',
'cost_price' => 'decimal:2',
'selling_price' => 'decimal:2',
];
public function category()
{
return $this->belongsTo(Category::class);
}
public function unit()
{
return $this->belongsTo(Unit::class);
}
public function stocks()
{
return $this->hasMany(Stock::class);
}
public function purchaseItems()
{
return $this->hasMany(PurchaseItem::class);
}
public function saleItems()
{
return $this->hasMany(SaleItem::class);
}
public function stockAdjustments()
{
return $this->hasMany(StockAdjustment::class);
}
// Helper: total stock across all warehouses
public function totalStock()
{
return $this->stocks()->sum('quantity');
}
}
Purchase Model (app/Models/Purchase.php):
class Purchase extends Model
{
protected $fillable = [
'supplier_id', 'warehouse_id', 'invoice_no', 'purchase_date',
'total_amount', 'paid_amount', 'due_amount', 'status',
];
protected $casts = [
'purchase_date' => 'date',
'total_amount' => 'decimal:2',
'paid_amount' => 'decimal:2',
'due_amount' => 'decimal:2',
];
public function supplier()
{
return $this->belongsTo(Supplier::class);
}
public function warehouse()
{
return $this->belongsTo(Warehouse::class);
}
public function items()
{
return $this->hasMany(PurchaseItem::class);
}
}
PurchaseItem Model (app/Models/PurchaseItem.php):
class PurchaseItem extends Model
{
protected $fillable = ['purchase_id', 'product_id', 'quantity', 'unit_cost', 'subtotal'];
protected $casts = [
'unit_cost' => 'decimal:2',
'subtotal' => 'decimal:2',
];
public function purchase()
{
return $this->belongsTo(Purchase::class);
}
public function product()
{
return $this->belongsTo(Product::class);
}
}
Sale Model (app/Models/Sale.php):
class Sale extends Model
{
protected $fillable = [
'customer_id', 'warehouse_id', 'invoice_no', 'sale_date',
'total_amount', 'paid_amount', 'due_amount', 'status',
];
protected $casts = [
'sale_date' => 'date',
'total_amount' => 'decimal:2',
'paid_amount' => 'decimal:2',
'due_amount' => 'decimal:2',
];
public function customer()
{
return $this->belongsTo(Customer::class);
}
public function warehouse()
{
return $this->belongsTo(Warehouse::class);
}
public function items()
{
return $this->hasMany(SaleItem::class);
}
}
SaleItem Model (app/Models/SaleItem.php):
class SaleItem extends Model
{
protected $fillable = ['sale_id', 'product_id', 'quantity', 'unit_price', 'subtotal'];
protected $casts = [
'unit_price' => 'decimal:2',
'subtotal' => 'decimal:2',
];
public function sale()
{
return $this->belongsTo(Sale::class);
}
public function product()
{
return $this->belongsTo(Product::class);
}
}
Stock Model (app/Models/Stock.php):
class Stock extends Model
{
protected $fillable = ['product_id', 'warehouse_id', 'quantity'];
public function product()
{
return $this->belongsTo(Product::class);
}
public function warehouse()
{
return $this->belongsTo(Warehouse::class);
}
}
StockAdjustment Model (app/Models/StockAdjustment.php):
class StockAdjustment extends Model
{
protected $fillable = [
'product_id', 'warehouse_id', 'type', 'quantity', 'reason', 'adjusted_by',
];
public function product()
{
return $this->belongsTo(Product::class);
}
public function warehouse()
{
return $this->belongsTo(Warehouse::class);
}
public function adjustedBy()
{
return $this->belongsTo(User::class, 'adjusted_by');
}
}
Database ER Diagram
Below is the entity relationship diagram showing how all 12 tables in our Inventory Management System connect to each other:
(Insert the ER diagram image here - exported from the Part 2 video)
Watch the Full Video Tutorial
📺 Part 1: Installation & Setup Guide - https://youtu.be/796FXMF6joY
📺 Part 2: Database Design - (embed YouTube video here)
📺 Part 3: Coming soon - subscribe to Webslesson on YouTube to get notified
What's Coming Next in This Series
- Part 3: Filament Panel branding and setup (custom logo, colors, navigation groups)
- Part 4: User roles and permissions
- Part 5 onward: Building Category, Product, Purchase, Sale, and Stock modules with Filament Resources
- Reports, dashboards, and final deployment
This blog post will be updated with new source code and explanation after every new part is published. Bookmark this page and check back regularly.
Frequently Asked Questions
Q1. Is this Inventory Management System free to download?
Yes, the complete source code for this project is free and will be shared part by part as the series progresses.
Q2. What is the minimum requirement to follow this tutorial?
You need PHP 8.3 or above, Composer, MySQL, and basic knowledge of Laravel.
Q3. Does this project support multiple warehouses?
Yes, the database is designed from the start to support multiple warehouses, with stock tracked separately for each warehouse.
Q4. Can I use this project for a real business?
Yes, once the series is complete, this project can be used as a starting point for a real inventory management application, with some customization based on your business needs.
If this tutorial helped you, don't forget to like the video, subscribe to Webslesson, and share this blog post with other developers who are learning Laravel and Filament.



