Tutorial 24 Jul 2026 12 Kali Dibaca

Cara Membuat Sistem Multi-Cabang Menggunakan Laravel

Gugun Nurdiansyah
Penulis
Cara Membuat Sistem Multi-Cabang Menggunakan Laravel

Apa Itu Sistem Multi-Cabang?

Sistem multi-cabang adalah aplikasi terpusat yang digunakan oleh beberapa lokasi usaha.

Setiap cabang dapat mengelola data operasionalnya sendiri, seperti:

  1. Produk dan stok.

  2. Penjualan.

  3. Pembelian.

  4. Pelanggan.

  5. Pengguna.

  6. Laporan.

Sementara itu, kantor pusat dapat memantau seluruh cabang melalui satu dashboard.

Sistem seperti ini cocok digunakan untuk minimarket, apotek, klinik, restoran, gudang, koperasi, dan perusahaan dengan banyak lokasi.

Struktur Dasar Multi-Cabang

Pendekatan paling sederhana adalah menggunakan satu database dan menambahkan kolom:

branch_id

pada setiap tabel operasional.

Contohnya:

branches
users
branch_user
products
transactions
customers
purchases

Setiap produk dan transaksi akan terhubung dengan cabang tertentu.

1. Membuat Tabel Cabang

Jalankan:

php artisan make:model Branch -m

Isi migration:

Schema::create('branches', function (Blueprint $table) {
    $table->id();
    $table->string('code')->unique();
    $table->string('name');
    $table->string('city')->nullable();
    $table->text('address')->nullable();
    $table->boolean('is_active')->default(true);
    $table->timestamps();
});

Kemudian jalankan:

php artisan migrate

2. Menghubungkan Pengguna dengan Cabang

Jika satu pengguna dapat mengakses beberapa cabang, buat tabel penghubung:

php artisan make:migration create_branch_user_table

Isi migration:

Schema::create('branch_user', function (Blueprint $table) {
    $table->id();

    $table->foreignId('branch_id')
        ->constrained()
        ->cascadeOnDelete();

    $table->foreignId('user_id')
        ->constrained()
        ->cascadeOnDelete();

    $table->boolean('is_default')->default(false);
    $table->timestamps();

    $table->unique(['branch_id', 'user_id']);
});

Relasi pada model User:

public function branches()
{
    return $this->belongsToMany(Branch::class)
        ->withPivot('is_default')
        ->withTimestamps();
}

Relasi pada model Branch:

public function users()
{
    return $this->belongsToMany(User::class)
        ->withPivot('is_default')
        ->withTimestamps();
}

3. Menambahkan branch_id pada Tabel Operasional

Setiap tabel yang datanya berbeda per cabang harus memiliki branch_id.

Contoh pada tabel produk:

Schema::table('products', function (Blueprint $table) {
    $table->foreignId('branch_id')
        ->after('id')
        ->constrained()
        ->cascadeOnDelete();

    $table->unique(['branch_id', 'sku']);
});

Tambahkan juga pada tabel lain, seperti:

transactions
customers
purchases
stock_movements
expenses

Jika aplikasi sudah memiliki data, tambahkan branch_id sebagai nullable terlebih dahulu. Setelah seluruh data lama memiliki cabang, barulah kolom dibuat wajib diisi.

4. Menentukan Cabang Aktif

Cabang aktif dapat disimpan pada session setelah pengguna login atau memilih cabang.

Contoh:

session([
    'active_branch_id' => $branchId,
]);

Sebelum menyimpan cabang, pastikan pengguna memang mempunyai akses:

$hasAccess = auth()->user()
    ->branches()
    ->whereKey($branchId)
    ->exists();

abort_unless($hasAccess, 403);

session([
    'active_branch_id' => $branchId,
]);

Jangan langsung mempercayai branch_id dari form tanpa memeriksa hak akses pengguna.

5. Membuat Middleware Cabang

Buat middleware:

php artisan make:middleware EnsureActiveBranch

Isi:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class EnsureActiveBranch
{
    public function handle(Request $request, Closure $next)
    {
        if (! $request->user()) {
            abort(401);
        }

        $branchId = session('active_branch_id');

        if (! $branchId) {
            abort(403, 'Silakan pilih cabang terlebih dahulu.');
        }

        $hasAccess = $request->user()
            ->branches()
            ->whereKey($branchId)
            ->exists();

        abort_unless(
            $hasAccess,
            403,
            'Anda tidak memiliki akses ke cabang ini.'
        );

        return $next($request);
    }
}

Gunakan middleware pada route:

Route::middleware([
    'auth',
    'branch',
])->group(function () {
    Route::resource('products', ProductController::class);
    Route::resource('transactions', TransactionController::class);
});

6. Membatasi Query Berdasarkan Cabang

Cara paling sederhana adalah menambahkan filter pada setiap query:

$products = Product::query()
    ->where('branch_id', session('active_branch_id'))
    ->get();

Ketika menambahkan data:

Product::create([
    'branch_id' => session('active_branch_id'),
    'sku' => $request->sku,
    'name' => $request->name,
    'price' => $request->price,
]);

Namun, hindari mengambil branch_id langsung dari request pengguna.

Gunakan cabang aktif yang sudah diverifikasi oleh middleware.

7. Menggunakan Global Scope

Agar tidak menulis filter cabang berulang kali, Anda dapat menggunakan global scope.

Contoh pada model Product:

protected static function booted(): void
{
    static::addGlobalScope('branch', function ($query) {
        if (session()->has('active_branch_id')) {
            $query->where(
                'branch_id',
                session('active_branch_id')
            );
        }
    });

    static::creating(function ($product) {
        $product->branch_id = session(
            'active_branch_id'
        );
    });
}

Dengan begitu, query berikut:

Product::all();

hanya menampilkan produk dari cabang aktif.

Global scope sebaiknya digunakan dengan hati-hati, terutama pada queue, scheduler, command, atau laporan semua cabang karena proses tersebut mungkin tidak memiliki session.

8. Membuat Fitur Ganti Cabang

Contoh controller sederhana:

public function switch(Request $request)
{
    $validated = $request->validate([
        'branch_id' => [
            'required',
            'integer',
            'exists:branches,id',
        ],
    ]);

    $branchId = (int) $validated['branch_id'];

    $hasAccess = $request->user()
        ->branches()
        ->whereKey($branchId)
        ->exists();

    abort_unless($hasAccess, 403);

    session([
        'active_branch_id' => $branchId,
    ]);

    return back()->with(
        'success',
        'Cabang berhasil diubah.'
    );
}

Route:

Route::post(
    '/switch-branch',
    [BranchController::class, 'switch']
)->middleware('auth');

9. Laporan Semua Cabang

Pengguna kantor pusat dapat diberikan akses untuk melihat seluruh cabang.

Contoh laporan penjualan:

$report = Transaction::query()
    ->select('branch_id')
    ->selectRaw('COUNT(*) as total_transactions')
    ->selectRaw('SUM(grand_total) as total_sales')
    ->groupBy('branch_id')
    ->with('branch')
    ->get();

Akses laporan semua cabang harus dibatasi menggunakan role atau permission.

10. Transfer Stok Antar-Cabang

Transfer stok harus mencatat:

  1. Cabang asal.

  2. Cabang tujuan.

  3. Produk.

  4. Jumlah.

  5. Pengirim.

  6. Penerima.

  7. Status transfer.

  8. Tanggal pengiriman dan penerimaan.

Gunakan database transaction agar pengurangan dan penambahan stok diproses sebagai satu kesatuan:

DB::transaction(function () {
    // Kurangi stok cabang asal.
    // Tambahkan stok cabang tujuan.
    // Simpan riwayat transfer.
});

Jika salah satu proses gagal, seluruh perubahan akan dibatalkan.

Keamanan Sistem Multi-Cabang

Beberapa hal yang perlu diperhatikan:

  1. Jangan mempercayai branch_id dari request.

  2. Periksa akses pengguna terhadap cabang.

  3. Lindungi route menggunakan middleware.

  4. Tambahkan filter cabang pada setiap query.

  5. Batasi laporan semua cabang.

  6. Gunakan transaksi database untuk transfer stok.

  7. Sertakan branch_id pada job queue.

  8. Uji sistem menggunakan beberapa akun dan cabang.

Jangan hanya menyembunyikan menu. Pengguna masih dapat mencoba membuka URL secara langsung.

Kesalahan yang Perlu Dihindari

Lupa Memfilter Data

Query tanpa branch_id dapat menampilkan data milik cabang lain.

Membiarkan Pengguna Mengirim branch_id

Pengguna dapat memanipulasi request untuk memasukkan data ke cabang yang tidak dimilikinya.

Menghapus Cabang Secara Langsung

Lebih aman menonaktifkan cabang menggunakan:

is_active = false

agar riwayat transaksi tetap tersimpan.

Mengandalkan Session pada Queue

Queue Worker tidak selalu memiliki session pengguna. Sertakan branch_id langsung ketika job dibuat.

Pengembangan Sistem Multi-Cabang Bersama Ovla Media

Ovla Media dapat membantu mengembangkan sistem multi-cabang untuk kebutuhan:

  1. Point of Sales.

  2. Inventory dan gudang.

  3. Klinik.

  4. Koperasi.

  5. Retail dan minimarket.

  6. Restoran dan kafe.

  7. Distributor.

  8. Dashboard kantor pusat.

  9. Transfer stok.

  10. Laporan operasional.

Sistem dapat dilengkapi dengan role dan permission, pembatasan akses cabang, monitoring stok, integrasi pembayaran, serta laporan terpusat.

Kesimpulan

Sistem multi-cabang Laravel dapat dibuat menggunakan satu database dengan menambahkan branch_id pada tabel operasional.

Pengguna dihubungkan dengan cabang melalui tabel branch_user, sedangkan middleware digunakan untuk memastikan pengguna hanya mengakses cabang yang diizinkan.

Query data harus selalu dibatasi berdasarkan cabang aktif:

Product::where(
    'branch_id',
    session('active_branch_id')
)->get();

Untuk sistem yang lebih besar, gunakan global scope, policy, database transaction, dan role permission agar data antar-cabang tetap aman dan tidak tercampur.

#Laravel #Sistem Multi-Cabang #Multi Branch Laravel #Branch Management #Aplikasi Multi-Cabang #Branch ID Laravel #Laravel Middleware #Laravel Global Scope #Inventory Multi-Cabang #POS Multi-Cabang #Transfer Stok #Laravel Database #Laravel Developer Indonesia #Web Development #Ovla Media #Jasa Laravel #Jasa Pembuatan Aplikasi Multi-Cabang
Beranda Produk Artikel
Konsultasi
OVLA

Navigasi Utama

Hubungi Kami

Mulai Konsultasi Sekarang