Pendahuluan
Penanganan sebelum dan sesudah penjualan sangat penting dilakukan oleh bidang usaha yang bergerak pada jasa jual beli, maka diperlukan sebuah fitur dimana customer bisa dengan mudah untuk melakukan claim return order apabila pesanan yang diterimanya tidak sesuai dengan apa yang ditawarkan oleh penjual. Selain itu, fitur untuk menandai barang apabila sudah diterima oleh customer juga penting bagi pemilik usaha agar mengetahui layanan yang diberikannya sudah selesai.
Seri membuat aplikasi e-commerce laravel 6 kali ini akan membahas beberapa hal, diantaranya, memperbaiki error dari seri artikel sebelumnya, membuat fitur approve order untuk memastikan pesanan sudah selesai dan auto approve dalam waktu +15 hari setelah barang dikirim untuk menghindari jika customer lupa untuk melakukan konfirmasi, dan yang terakhir adalah fasilitas untuk complaint serta meminta return order dengan ketentuan customer mengirimkan alasan dilengkapi foto barang pesanan.
Baca Juga: Aplikasi E-Commerce Laravel 6 #14: Kelola Pesanan (Admin) & Broadcast Resi
[Issue] Checkout Page
Masalah yang dihadapi adalah pada seri sebelumnya kita membuat kolom input-an email otomatis terisi apabila customer sudah login, masalahnya adalah akan terjadi error ketika customer belum login, maka buka file ecommerce/checkout.blade.php
dan modifikasi pada bagian kolom email.
<div class="col-md-6 form-group p_star">
<label for="">Email</label>
@if (auth()->guard('customer')->check())
<input type="email" class="form-control" id="email" name="email"
value="{{ auth()->guard('customer')->user()->email }}"
required {{ auth()->guard('customer')->check() ? 'readonly':'' }}>
@else
<input type="email" class="form-control" id="email" name="email"
required>
@endif
<p class="text-danger">{{ $errors->first('email') }}</p>
</div>
Approve Order By Customer
Ketika customer mengakses halaman untuk melihat seluruh list pesanan yang dimilikinya, maka sebuah tombol untuk menandai pesanan sudah diterima atau belum akan muncul disaat pesanan tersebut ber-status shippping (red: 3). Tahap pertama kita buat tombolnya terlebih dahulu, buka file ecommerce/orders/index.blade.php
dan modifikasi tag yang ada di kolom button.
<form action="{{ route('customer.order_accept') }}"
class="form-inline"
onsubmit="return confirm('Kamu Yakin?');" method="post">
@csrf
<!-- TOMBOL VIEW ORDER KITA BUNGKUS JG DENGAN FORM AGAR RAPI -->
<a href="{{ route('customer.view_order', $row->invoice) }}" class="btn btn-primary btn-sm mr-1">Detail</a>
<input type="hidden" name="order_id" value="{{ $row->id }}">
@if ($row->status == 3)
<button class="btn btn-success btn-sm">Terima</button>
<!-- TOMBOL RETURN AKAN DITEMPATKAN DISINI PADA SUB-BAB SELANJUTNYA -->
@endif
</form>
Penjelasan: Jadi kita menggunakan onsubmit pada form, dimana fungsi ini akan menampilkan popup konfirmasi sebelum mengirimkan form request. Kemudian data yang dikirimkan hanya order_id
karena data berdasarkan id ini akan diubah status-nya menjadi 4 atau diterima.
Masih dengan file yang sama, tambahkan alert success berikut ini setelah alert danger di dalam class card-body
.
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
Tugas selanjutnya adalah meng-handle request yang dikirimkan oleh customer, buka file Ecommerce/OrderController
dan tambahkan method
public function acceptOrder(Request $request)
{
//CARI DATA ORDER BERDASARKAN ID
$order = Order::find($request->order_id);
//VALIDASI KEPEMILIKAN
if (!\Gate::forUser(auth()->guard('customer')->user())->allows('order-view', $order)) {
return redirect()->back()->with(['error' => 'Bukan Pesanan Kamu']);
}
//UBAH STATUSNYA MENJADI 4
$order->update(['status' => 4]);
//REDIRECT KEMBALI DENGAN MENAMPILKAN ALERT SUCCESS
return redirect()->back()->with(['success' => 'Pesanan Dikonfirmasi']);
}
Definisikan routing dari method di atas, buka file routes/web.php
dan tambahkan baris berikut di dalam routing group yang menggunakan middleware customer
Route::post('orders/accept', 'OrderController@acceptOrder')->name('customer.order_accept');
Return Order
Skenario dari fitur ini adalah sebuah tombol return akan ditampilkan apabila status order sama dengan 3 atau shipping & belum pernah di-return sebelumnya, kemudian apabila tombol tersebut di-klik maka akan diarahkan ke halaman yang berisi sebuah form untuk memasukkan informasi terkait return dari produk tersebut.
Tahapan pertama adalah membuat tombolnya terlebih dahulu, buka file ecommerce/orders/index.blade.php
dan modifikasi kembali kolom untuk menampilkan tombol menjadi
<form action="{{ route('customer.order_accept') }}"
class="form-inline"
onsubmit="return confirm('Kamu Yakin?');" method="post">
@csrf
<a href="{{ route('customer.view_order', $row->invoice) }}" class="btn btn-primary btn-sm mr-1">Detail</a>
<input type="hidden" name="order_id" value="{{ $row->id }}">
<!-- KONDISINYA DITAMBAHKAN, JIKA RETURN_COUNT = 0 -->
@if ($row->status == 3 && $row->return_count == 0)
<button class="btn btn-success btn-sm">Terima</button>
<!-- TOMBOL UNTUK MENGARAH KE HALAMAN RETURN -->
<a href="{{ route('customer.order_return', $row->invoice) }}" class="btn btn-danger btn-sm mt-1">Return</a>
@endif
</form>
Mungkin ada pertanyaan, return_count
di dapatkan dari mana? Dari code berikut yang akan kita lengkapi, dimana data tersebut adalah jumlah data yang berelasi dengan order terkait. Buat relasinya terlebih dahulu, buka file Order.php
dan tambahkan method
public function return()
{
return $this->hasOne(OrderReturn::class);
}
Untuk me-load relasi diatas, buka file Ecommerce/OrderController.php
dan cari method index()
lalu modifikasi menjadi
public function index()
{
$orders = Order::withCount(['return'])->where('customer_id', auth()->guard('customer')->user()->id)
->orderBy('created_at', 'DESC')->paginate(10);
return view('ecommerce.orders.index', compact('orders'));
}
Note: Ditambahkan withCount()
untuk menghasilkan objek baru dengan format namarelasi_count
.
Adapun untuk langkah selanjutnya adalah masih dengan file yang sama, tambahkan method berikut untuk me-load view form return.
public function returnForm($invoice)
{
//LOAD DATA BERDASARKAN INVOICE
$order = Order::where('invoice', $invoice)->first();
//LOAD VIEW RETURN.BLADE.PHP DAN PASSING DATA ORDER
return view('ecommerce.orders.return', compact('order'));
}
Buat file baru bernama return.blade.php
di dalam folder ecommerce/orders
dan tambahkan tag berikut
@extends('layouts.ecommerce')
@section('title')
<title>Return {{ $order->invoice }} - DW Ecommerce</title>
@endsection
@section('content')
<!--================Home Banner Area =================-->
<section class="banner_area">
<div class="banner_inner d-flex align-items-center">
<div class="container">
<div class="banner_content text-center">
<h2>Return {{ $order->invoice }}</h2>
<div class="page_link">
<a href="{{ url('/') }}">Home</a>
<a href="{{ route('customer.orders') }}">Return {{ $order->invoice }}</a>
</div>
</div>
</div>
</div>
</section>
<section class="login_box_area p_120">
<div class="container">
<div class="row">
<div class="col-md-3">
@include('layouts.ecommerce.module.sidebar')
</div>
<div class="col-md-9">
<div class="card">
<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('customer.return', $order->id) }}" method="post" enctype="multipart/form-data">
@csrf
<input type="hidden" name="_method" value="PUT">
<div class="form-group">
<label for="">Alasan Return</label>
<textarea name="reason" cols="5" rows="5" class="form-control" required></textarea>
</div>
<div class="form-group">
<label for="">Refund Transfer</label>
<input type="text" name="refund_transfer" class="form-control" required>
</div>
<div class="form-group">
<label for="">Foto</label>
<input type="file" name="photo" class="form-control" required>
</div>
<button class="btn btn-primary">Kirim</button>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
@endsection
Penjelasan: Jadi code di atas adalah tag untuk menampilkan form inputan yang terdiri dari alasan complaint, tujuan pengembalian dana apabila return disetujui, dan yang terakhir adalah foto barang yang di-complaint.
Saatnya untuk meng-handle permintaan yang dikirimkan, adapun routing-nya akan dibuat bersamaan dengan routing proses complaint agar tidak bolak-balik membuka file routing. Di dalam file Ecommerce/OrderController.php
tambahkan method
public function processReturn(Request $request, $id)
{
//LAKUKAN VALIDASI DATA
$this->validate($request, [
'reason' => 'required|string',
'refund_transfer' => 'required|string',
'photo' => 'required|image|mimes:jpg,png,jpeg'
]);
//CARI DATA RETURN BERDASARKAN order_id YANG ADA DITABLE ORDER_RETURNS NANTINYA
$return = OrderReturn::where('order_id', $id)->first();
//JIKA DITEMUKAN, MAKA TAMPILKAN NOTIFIKASI ERROR
if ($return) return redirect()->back()->with(['error' => 'Permintaan Refund Dalam Proses']);
//JIKA TIDAK, LAKUKAN PENGECEKAN UNTUK MEMASTIKAN FILE FOTO DIKIRIMKAN
if ($request->hasFile('photo')) {
//GET FILE
$file = $request->file('photo');
//GENERATE NAMA FILE BERDASARKAN TIME DAN STRING RANDOM
$filename = time() . Str::random(5) . '.' . $file->getClientOriginalExtension();
//KEMUDIAN UPLOAD KE DALAM FOLDER STORAGE/APP/PUBLIC/RETURN
$file->storeAs('public/return', $filename);
//DAN SIMPAN INFORMASINYA KE DALAM TABLE ORDER_RETURNS
OrderReturn::create([
'order_id' => $id,
'photo' => $filename,
'reason' => $request->reason,
'refund_transfer' => $request->refund_transfer,
'status' => 0
]);
//LALU TAMPILKAN NOTIFIKASI SUKSES
return redirect()->back()->with(['success' => 'Permintaan Refund Dikirim']);
}
}
Jangan lupa untuk menambahkan use statement
use App\OrderReturn;
use Illuminate\Support\Str;
Dua langkah terakhir, kita definisikan routing-nya dengan menambahkan baris route berikut di dalam routes/web.php
Route::get('orders/return/{invoice}', 'OrderController@returnForm')->name('customer.order_return');
Route::put('orders/return/{invoice}', 'OrderController@processReturn')->name('customer.return');
Dan bagian terakhir adalah men-generate table-nya, pada command line, jalankan command
php artisan make:model OrderReturn -m
Kemudian buka file migration yang baru saja di-generate dan modifikasi menjadi
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateOrderReturnsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_returns', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('order_id');
$table->string('photo');
$table->text('reason');
$table->string('refund_transfer');
$table->char('status', 1)->default(0)->comment('0: pending, 1: approve, 2: cancel');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('order_returns');
}
}
Lalu di dalam file OrderReturn.php
juga modifikasi dengan menambahkan sebaris code berikut untuk mengizinkan mass assignment menambahkan record baru.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class OrderReturn extends Model
{
protected $guarded = [];
}
Baca Juga: Aplikasi E-Commerce Laravel 6 #13: Membuat Faktur PDF
Kesimpulan
Seluruh proses yang dibutuhkan dari sisi customer sudah dipenuhi, sehingga proses verifikasi return order akan dikerjakan dari sisi administrator dan fitur ini akan dikerjakan pada artikel berikutnya. Sepanjang artikel ini kita sudah belajar bagaimana menciptakan alur baru yang tidak ada di-schema database pada seri pertama dimana pesannya adalah bahwa keadaan apapun mungkin terjadi sesuai dengan perkembangan bisnis, maka sebagai seorang programmer kita harus bisa memahami kemana alur bisnis berjalan dan menerjemahkannya ke dalam code agar dapat diwujudkan dalam bentuk aplikasi.
Adapun dokumentasi code dari artikel di atas bisa dilihat di Github.
Comments