Showing posts with label laravel inventory management source code. Show all posts
Showing posts with label laravel inventory management source code. Show all posts

Tuesday, 4 August 2026

Laravel 13 + Filament 5 Inventory Management System - Complete Tutorial Series (2026)

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


TechnologyPurpose
Laravel 13Backend framework
Filament 5Admin panel builder
MySQLDatabase
Livewire 4Reactive 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:

  1. categories - product categories, with parent/child support
  2. units - measuring units (pcs, kg, box, etc.)
  3. suppliers - vendors we purchase stock from
  4. customers - people we sell products to
  5. warehouses - physical stock locations
  6. products - the core product table
  7. purchases - purchase order records
  8. purchase_items - products inside each purchase order
  9. sales - sale/invoice records
  10. sale_items - products inside each sale
  11. stocks - current stock quantity per product per warehouse
  12. stock_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 StockAdjustment

Each 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.