Aplikasi E-Commerce Laravel 6 #17: Laporan Periode Date Range

Aplikasi E-Commerce Laravel 6 #17: Laporan Periode Date Range

Pendahuluan

Laporan memiliki peranan penting dalam mengevaluasi sebuah bisnis, termasuk dalam hal ini adalah transaksi jual beli. Fitur ini tidak lebih dari sekedar feed order, akan tetapi ada pembaca DaengWeb yang melakukan request untuk membuat laporan berdasarkan periode date range di Laravel, sehingga melalui artikel ini kita akan belajar bagaimana membuat sebuah laporan dimana skenario yang diinginkan adalah ketika halaman laporan diakses maka akan menampilkan seluruh pesanan / order dengan range 30/31 hari berdasarkan bulan saat user mengakses fitur ini.

Adapun fitur lainnya adalah sebuah fungsi agar user bisa men-filter data dengan range yang ditentukannya, kemudian juga dilengkapi dengan fasilitas export ke PDF.

Baca Juga: Aplikasi E-Commerce Laravel 6 #16: Integrasi Telegram Bot

Laporan Order Dengan Date Range

Laporan berdasarkan periode yang akan kita selesaikan pertama kali adalah feed atau data order, buka file HomeController.php dan tambahkan method

public function orderReport()
{
    //INISIASI 30 HARI RANGE SAAT INI JIKA HALAMAN PERTAMA KALI DI-LOAD
    //KITA GUNAKAN STARTOFMONTH UNTUK MENGAMBIL TANGGAL 1
    $start = Carbon::now()->startOfMonth()->format('Y-m-d H:i:s');
    //DAN ENDOFMONTH UNTUK MENGAMBIL TANGGAL TERAKHIR DIBULAN YANG BERLAKU SAAT INI
    $end = Carbon::now()->endOfMonth()->format('Y-m-d H:i:s');

    //JIKA USER MELAKUKAN FILTER MANUAL, MAKA PARAMETER DATE AKAN TERISI
    if (request()->date != '') {
        //MAKA FORMATTING TANGGALNYA BERDASARKAN FILTER USER
        $date = explode(' - ' ,request()->date);
        $start = Carbon::parse($date[0])->format('Y-m-d') . ' 00:00:01';
        $end = Carbon::parse($date[1])->format('Y-m-d') . ' 23:59:59';
    }

    //BUAT QUERY KE DB MENGGUNAKAN WHEREBETWEEN DARI TANGGAL FILTER
    $orders = Order::with(['customer.district'])->whereBetween('created_at', [$start, $end])->get();
    //KEMUDIAN LOAD VIEW
    return view('report.order', compact('orders'));
}

Jangan lupa tambahkan use statement:

use App\Order;
use Carbon\Carbon;

Kemudian buat file order.blade.php di dalam folder resources/views/report dan tambahkan tag berikut

@extends('layouts.admin')

@section('title')
    <title>Laporan Order</title>
@endsection

@section('content')
<main class="main">
    <ol class="breadcrumb">
        <li class="breadcrumb-item">Home</li>
        <li class="breadcrumb-item active">Laporan Order</li>
    </ol>
    <div class="container-fluid">
        <div class="animated fadeIn">
            <div class="row">
                <div class="col-md-12">
                    <div class="card">
                        <div class="card-header">
                            <h4 class="card-title">
                                Laporan Order
                            </h4>
                        </div>
                        <div class="card-body">
                            @if (session('success'))
                                <div class="alert alert-success">{{ session('success') }}</div>
                            @endif

                            @if (session('error'))
                                <div class="alert alert-danger">{{ session('error') }}</div>
                            @endif

                            <!-- FORM UNTUK FILTER BERDASARKAN DATE RANGE -->
                            <form action="{{ route('report.order') }}" method="get">
                                <div class="input-group mb-3 col-md-4 float-right">
                                    <input type="text" id="created_at" name="date" class="form-control">
                                    <div class="input-group-append">
                                        <button class="btn btn-secondary" type="submit">Filter</button>
                                    </div>
                                    <a target="_blank" class="btn btn-primary ml-2" id="exportpdf">Export PDF</a>
                                </div>
                            </form>
                            <div class="table-responsive">
                                <!-- TAMPILKAN DATA YANG BERHASIL DIFILTER -->
                                <table class="table table-hover table-bordered">
                                    <thead>
                                        <tr>
                                            <th>InvoiceID</th>
                                            <th>Pelanggan</th>
                                            <th>Subtotal</th>
                                            <th>Tanggal</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        @forelse ($orders as $row)
                                        <tr>
                                            <td><strong>{{ $row->invoice }}</strong></td>
                                            <td>
                                                <strong>{{ $row->customer_name }}</strong><br>
                                                <label><strong>Telp:</strong> {{ $row->customer_phone }}</label><br>
                                                <label><strong>Alamat:</strong> {{ $row->customer_address }} {{ $row->customer->district->name }} - {{  $row->customer->district->city->name}}, {{ $row->customer->district->city->province->name }}</label>
                                            </td>
                                            <td>Rp {{ number_format($row->subtotal) }}</td>
                                            <td>{{ $row->created_at->format('d-m-Y') }}</td>
                                        </tr>
                                        @empty
                                        <tr>
                                            <td colspan="6" class="text-center">Tidak ada data</td>
                                        </tr>
                                        @endforelse
                                    </tbody>
                                </table>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</main>
@endsection

<!-- KITA GUNAKAN LIBRARY DATERANGEPICKER -->
@section('js')
    <script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
    <script>
        //KETIKA PERTAMA KALI DI-LOAD MAKA TANGGAL NYA DI-SET TANGGAL SAA PERTAMA DAN TERAKHIR DARI BULAN SAAT INI
        $(document).ready(function() {
            let start = moment().startOf('month')
            let end = moment().endOf('month')

            //KEMUDIAN TOMBOL EXPORT PDF DI-SET URLNYA BERDASARKAN TGL TERSEBUT
            $('#exportpdf').attr('href', '/administrator/reports/order/pdf/' + start.format('YYYY-MM-DD') + '+' + end.format('YYYY-MM-DD'))

            //INISIASI DATERANGEPICKER
            $('#created_at').daterangepicker({
                startDate: start,
                endDate: end
            }, function(first, last) {
                //JIKA USER MENGUBAH VALUE, MANIPULASI LINK DARI EXPORT PDF
                $('#exportpdf').attr('href', '/administrator/reports/order/pdf/' + first.format('YYYY-MM-DD') + '+' + last.format('YYYY-MM-DD'))
            })
        })
    </script>
@endsection()

Kini saatnya mendefinisikan routing-nya agar bisa diakses melalui browser, buka file routes/web.php dan tambahkan baris berikut di dalam route group dengan prefix administrator

Route::group(['prefix' => 'reports'], function() {
    Route::get('/order', 'HomeController@orderReport')->name('report.order');
    Route::get('/order/pdf/{daterange}', 'HomeController@orderReportPdf')->name('report.order_pdf');
  
    // [.. ROUTING LAINNYA ..]
});

Proses untuk menampilkan data order berdasarkan periode sudah selesai, maka kini langkah selanjutnya adalah men-generate pdf dimana routing-nya sudah kita buat. Buka file HomeController.php dan tambahkan method orderReportPdf

public function orderReportPdf($daterange)
{
    $date = explode('+', $daterange); //EXPLODE TANGGALNYA UNTUK MEMISAHKAN START & END
    //DEFINISIKAN VARIABLENYA DENGAN FORMAT TIMESTAMPS
    $start = Carbon::parse($date[0])->format('Y-m-d') . ' 00:00:01';
    $end = Carbon::parse($date[1])->format('Y-m-d') . ' 23:59:59';

    //KEMUDIAN BUAT QUERY BERDASARKAN RANGE CREATED_AT YANG TELAH DITETAPKAN RANGENYA DARI $START KE $END
    $orders = Order::with(['customer.district'])->whereBetween('created_at', [$start, $end])->get();
    //LOAD VIEW UNTUK PDFNYA DENGAN MENGIRIMKAN DATA DARI HASIL QUERY
    $pdf = PDF::loadView('report.order_pdf', compact('orders', 'date'));
    //GENERATE PDF-NYA
    return $pdf->stream();
}

Jangan lupa menambahkan use statement:

use PDF;

Langkah terakhir adalah menyediakan file order_pdf.blade.php di dalam folder report dan tambahkan tag berikut

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Order PDF</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</head>
<body>
    <h5>Laporan Order Periode ({{ $date[0] }} - {{ $date[1] }})</h5>
    <hr>
    <table width="100%" class="table-hover table-bordered">
        <thead>
            <tr>
                <th>InvoiceID</th>
                <th>Pelanggan</th>
                <th>Subtotal</th>
                <th>Tanggal</th>
            </tr>
        </thead>
        <tbody>
            @php $total = 0; @endphp
            @forelse ($orders as $row)
                <tr>
                    <td><strong>{{ $row->invoice }}</strong></td>
                    <td>
                        <strong>{{ $row->customer_name }}</strong><br>
                        <label><strong>Telp:</strong> {{ $row->customer_phone }}</label><br>
                        <label><strong>Alamat:</strong> {{ $row->customer_address }} {{ $row->customer->district->name }} - {{  $row->customer->district->city->name}}, {{ $row->customer->district->city->province->name }}</label>
                    </td>
                    <td>Rp {{ number_format($row->subtotal) }}</td>
                    <td>{{ $row->created_at->format('d-m-Y') }}</td>
                </tr>

                @php $total += $row->subtotal @endphp
            @empty
            <tr>
                <td colspan="6" class="text-center">Tidak ada data</td>
            </tr>
            @endforelse
        </tbody>
        <tfoot>
            <tr>
                <td colspan="2">Total</td>
                <td>Rp {{ number_format($total) }}</td>
                <td></td>
            </tr>
        </tfoot>
    </table>
</body>
</html>

Note: Tidak ada yang perlu dijelaskan dari code di atas karena hanya proses menampilkan data ke dalam tag table.

Laporan Return Dengan Date Range

Fitur ini hampir sama dengan fitur sebelumnya, prosesnya juga sama saja, yang membedakan hanyalah query-nya saja dimana data yang akan ditampilkan adalah data order yang memiliki return.

Agar prosesnya lebih cepat, kita tambahkan dua method sekaligus yang meng-handle tampilan dibrowser dan pdf, buka file HomeController.php dan tambahkan

public function returnReport()
{
    $start = Carbon::now()->startOfMonth()->format('Y-m-d H:i:s');
    $end = Carbon::now()->endOfMonth()->format('Y-m-d H:i:s');

    if (request()->date != '') {
        $date = explode(' - ' ,request()->date);
        $start = Carbon::parse($date[0])->format('Y-m-d') . ' 00:00:01';
        $end = Carbon::parse($date[1])->format('Y-m-d') . ' 23:59:59';
    }

    $orders = Order::with(['customer.district'])->has('return')->whereBetween('created_at', [$start, $end])->get();
    return view('report.return', compact('orders'));
}

public function returnReportPdf($daterange)
{
    $date = explode('+', $daterange);
    $start = Carbon::parse($date[0])->format('Y-m-d') . ' 00:00:01';
    $end = Carbon::parse($date[1])->format('Y-m-d') . ' 23:59:59';

    $orders = Order::with(['customer.district'])->has('return')->whereBetween('created_at', [$start, $end])->get();
    $pdf = PDF::loadView('report.return_pdf', compact('orders', 'date'));
    return $pdf->stream();
}

Note: Yang membedakan code di atas dari code sebelumnya adalah adanya penambahan fungsi has() pada query. Dimana fungsi has ini akan mengecek apakah memiliki data yang terkait dengan table lain atau tidak, jika iya akan ditampilkan, jika tidak maka akan diabaikan.

Kemudian view-nya ada dua, pertama buat file return.blade.php di dalam folder report dan tambahkan method

@extends('layouts.admin')

@section('title')
    <title>Laporan Return</title>
@endsection

@section('content')
<main class="main">
    <ol class="breadcrumb">
        <li class="breadcrumb-item">Home</li>
        <li class="breadcrumb-item active">Laporan Return</li>
    </ol>
    <div class="container-fluid">
        <div class="animated fadeIn">
            <div class="row">
                <div class="col-md-12">
                    <div class="card">
                        <div class="card-header">
                            <h4 class="card-title">
                                Laporan Return
                            </h4>
                        </div>
                        <div class="card-body">
                            @if (session('success'))
                                <div class="alert alert-success">{{ session('success') }}</div>
                            @endif

                            @if (session('error'))
                                <div class="alert alert-danger">{{ session('error') }}</div>
                            @endif

                            <form action="{{ route('report.return') }}" method="get">
                                <div class="input-group mb-3 col-md-4 float-right">
                                    <input type="text" id="created_at" name="date" class="form-control">
                                    <div class="input-group-append">
                                        <button class="btn btn-secondary" type="submit">Filter</button>
                                    </div>
                                    <a target="_blank" class="btn btn-primary ml-2" id="exportpdf">Export PDF</a>
                                </div>
                            </form>
                            <div class="table-responsive">
                                <table class="table table-hover table-bordered">
                                    <thead>
                                        <tr>
                                            <th>InvoiceID</th>
                                            <th>Pelanggan</th>
                                            <th>Subtotal</th>
                                            <th>Tanggal</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        @forelse ($orders as $row)
                                        <tr>
                                            <td><strong>{{ $row->invoice }}</strong></td>
                                            <td>
                                                <strong>{{ $row->customer_name }}</strong><br>
                                                <label><strong>Telp:</strong> {{ $row->customer_phone }}</label><br>
                                                <label><strong>Alamat:</strong> {{ $row->customer_address }} {{ $row->customer->district->name }} - {{  $row->customer->district->city->name}}, {{ $row->customer->district->city->province->name }}</label>
                                            </td>
                                            <td>Rp {{ number_format($row->subtotal) }}</td>
                                            <td>{{ $row->created_at->format('d-m-Y') }}</td>
                                        </tr>
                                        @empty
                                        <tr>
                                            <td colspan="6" class="text-center">Tidak ada data</td>
                                        </tr>
                                        @endforelse
                                    </tbody>
                                </table>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</main>
@endsection

@section('js')
    <script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
    <script>
        $(document).ready(function() {
            let start = moment().startOf('month')
            let end = moment().endOf('month')

            $('#exportpdf').attr('href', '/administrator/reports/return/pdf/' + start.format('YYYY-MM-DD') + '+' + end.format('YYYY-MM-DD'))

            $('#created_at').daterangepicker({
                startDate: start,
                endDate: end
            }, function(first, last) {
                $('#exportpdf').attr('href', '/administrator/reports/return/pdf/' + first.format('YYYY-MM-DD') + '+' + last.format('YYYY-MM-DD'))
            })
        })
    </script>
@endsection()

Bagian kedua adalah file return_pdf.blade.php untuk meng-handle tampilan pdf-nya, kemudian tambahkan tag berikut

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Return PDF</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</head>
<body>
    <h5>Laporan Return Periode ({{ $date[0] }} - {{ $date[1] }})</h5>
    <hr>
    <table width="100%" class="table-hover table-bordered">
        <thead>
            <tr>
                <th>InvoiceID</th>
                <th>Pelanggan</th>
                <th>Subtotal</th>
                <th>Tanggal</th>
            </tr>
        </thead>
        <tbody>
            @php $total = 0; @endphp
            @forelse ($orders as $row)
                <tr>
                    <td><strong>{{ $row->invoice }}</strong></td>
                    <td>
                        <strong>{{ $row->customer_name }}</strong><br>
                        <label><strong>Telp:</strong> {{ $row->customer_phone }}</label><br>
                        <label><strong>Alamat:</strong> {{ $row->customer_address }} {{ $row->customer->district->name }} - {{  $row->customer->district->city->name}}, {{ $row->customer->district->city->province->name }}</label>
                    </td>
                    <td>Rp {{ number_format($row->subtotal) }}</td>
                    <td>{{ $row->created_at->format('d-m-Y') }}</td>
                </tr>

                @php $total += $row->subtotal @endphp
            @empty
            <tr>
                <td colspan="6" class="text-center">Tidak ada data</td>
            </tr>
            @endforelse
        </tbody>
        <tfoot>
            <tr>
                <td colspan="2">Total</td>
                <td>Rp {{ number_format($total) }}</td>
                <td></td>
            </tr>
        </tfoot>
    </table>
</body>
</html>

Langkah terakhir adalah mendefinisikan routing dari laporan return, buka file routes/web.php dan definisikan routing berikut di dalam route group reports

Route::get('/return', 'HomeController@returnReport')->name('report.return');
Route::get('/return/pdf/{daterange}', 'HomeController@returnReportPdf')->name('report.return_pdf');

Menu Navigasi

Agar kedua fitur yang sudah kita buat di atas bisa diakses dari sidebar menu sehingga memudahkan user untuk mengaksesnya, maka kita perlu menambahkan data link dari masing-masing fitur tersebut. Buka file sidebar.blade.php yang berada di dalam folder resources/views/layouts/module dan modifikasi tag pengaturan menjadi

<li class="nav-item nav-dropdown">
    <a class="nav-link nav-dropdown-toggle" href="javascript">
        <i class="nav-icon icon-settings"></i> Laporan
    </a>
    <ul class="nav-dropdown-items">
        <li class="nav-item">
            <a class="nav-link" href="{{ route('report.order') }}">
                <i class="nav-icon icon-puzzle"></i> Order
            </a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{ route('report.return') }}">
                <i class="nav-icon icon-puzzle"></i> Return
            </a>
        </li>
    </ul>
</li>

Baca Juga: Aplikasi E-Commerce Laravel 6 #15: Complaint Return Order

Kesimpulan

Kendala utama dari para pembelajar yang baru saja memulai adalah terpaku dengan kata, misalnya saja ingin membuat fitur range filtering, maka kebanyakan dari kita adalah mencari kata kunci tersebut untuk kemudian dilihat bagaimana cara membuatnya.

Padahal sebelumnya kita sudah belajar bagaimana menampilkan data order dan juga sudah belajar bagaimana membuat PDF pada seri membuat faktur, kita hanya perlu menggabungkan kedua metode tersebut menjadi satu kesatuan agar menciptakan fitur baru.

Adapun dokumentasi code dari artikel ini bisa dilihat di Github.

Category:
Share:

Comments