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