Aplikasi Laundry (Laravel 5.8 - Vue.js - SPA) #10: Modul Transaksi Part 2

Aplikasi Laundry (Laravel 5.8 - Vue.js - SPA) #10: Modul Transaksi Part 2

Pendahuluan

Melengkapi modul transaksi dari part sebelumnya, dimana kita telah membuat sebuah form untuk mencatat transaksi baru. Akan tetapi masih ada beberapa hal yang kurang dan perlu dilengkapi, maka pada artikel ini kita akan fokus untuk melengkapi form add new customer langsung pada halaman transaksi dan juga menyelesaikan bug yang ditemukan yakni dengan menambahkan jangka waktu atau lama pengerjaan untuk masing-masing product/item layanan laundry.

Adapun schema yang diinginkan untuk form customer adalah ketika user ingin menambahkan customer baru, maka terdapat sebuah tombol untuk membuka form tersebut. Jika penambahan data customer berhasil dilakukan, maka secara otomatis pada select box akan memilih customer yang baru saja ditambahkan.

Sedangkan untuk product/item layanan laundry, kita akan menambahkan field baru ke dalam table laundry_prices untuk mencatat lama pengerjaan dalam satuan jam maupun hari.

Baca Juga: Aplikasi Laundry (Laravel 5.8 - Vue.js - SPA) #9: Modul Transaksi Part 1

Add New Customer

Bagian pertama yang akan kita selesaikan adalah dynamic form untuk mengisi data customer, buka file resources/js/pages/transaction/Form.vue dan berikut beberapa bagian yang perlu dimodifikasi:

<!-- [.. CODE SEBELUMNYA ..] -->

<div class="form-group" :class="{ 'has-error': errors.customer_id }">
  	
    <!-- [.. EDIT BAGIAN INI ..] -->
    <label for="">Customer <sup><a href="javascript:void(0)" @click="newCustomer">New Customer</a></sup></label>
  
    <v-select :options="customers.data"
        v-model="transactions.customer_id"
        @search="onSearch" 
        label="name"
        placeholder="Masukkan Kata Kunci" 
        :filterable="false">
        <template slot="no-options">
            Masukkan Kata Kunci
        </template>
        <template slot="option" slot-scope="option">
            {{ option.name }}
        </template>
    </v-select>
    <p class="text-danger" v-if="errors.customer_id">{{ errors.customer_id[0] }}</p>
</div>

<!-- [.. CODE SETELAHNYA ..] -->

Penjelasan: Pada potongan code diatas hanya menambahkan sebuah link atau tombol baru dimana ketika diklik akan menjalankan method newCustomer().

Note: Beberapa potongan code selanjutnya masih seputar file Form.vue sampai ada instruksi untuk membuka file lainnya.

Tambahkan method newCustomer tepat di dalam property method:

methods: {
	//[.. CODE SEBELUMNYA ..]
	newCustomer() {
    this.isForm = true //MENGUBAH VALUE isForm MENJADI TRUE
  },
}

Salah satu keuntungan menggunakan reusable code adalah kita dapat menggunakannya ketika dibutuhkan, contohnya adalah form customer dimana telah dipisahkan ke dalam file resources/js/pages/customers/Form.vue (red: hanya info, buka instruksi untuk membuka file tersebut). Jadi form tersebut akan kita gunakan di dalam form transaksi, tambahkan code berikut:

<!-- [.. CODE SEBELUMNYA ..] -->
<div class="col-md-6" v-if="transactions.customer_id != null && !isForm">
    <table>
        <tr>
            <th width="30%">NIK </th>
            <td width="5%">:</td>
            <td>{{ transactions.customer_id.nik }}</td>
        </tr>
        <tr>
            <th>No Telp </th>
            <td>:</td>
            <td>{{ transactions.customer_id.phone }}</td>
        </tr>
        <tr>
            <th>Alamat </th>
            <td>:</td>
            <td>{{ transactions.customer_id.address }}</td>
        </tr>
        <tr>
            <th>Deposit </th>
            <td>:</td>
            <td>Rp {{ transactions.customer_id.deposit }}</td>
        </tr>
        <tr>
            <th>Point </th>
            <td>:</td>
            <td>{{ transactions.customer_id.point }}</td>
        </tr>
    </table>
</div>
<!-- [.. CODE SEBELUMNYA ..] -->

<!-- CUKUP TAMBAHKAN CODE INI -->
<div class="col-md-6" v-if="isForm">
    <h4>Add New Customer</h4>
    <form-customer />
    <button class="btn btn-primary btn-s" @click="addCustomer">Save</button>
</div>
<!-- CUKUP TAMBAHKAN CODE INI -->

Penjelasan: <form-customer> adalah custom tag yang kita dapatkan dari local components (red: setelah ini akan di-import dan didefinisikan) dan tombol save ketika ditekan akan menjalankan method addCustomer().

Import form customer dengan menambahkan code berikut pada bagian import statement:

import FormCustomer from '../customers/Form.vue'

Kemudian definisikan component diatas dengan menambahkan code berikut:

components: {
    vSelect,
    'form-customer': FormCustomer //TAMBAHKAN BAGIAN INI
}

Bagian terakhir adalah dengan mendifinisikan method addCustomer():

methods: {
	...mapActions('customer', ['submitCustomer']), //KITA GUNAKAN FUNGSI YANG ADA DI MODULE CUSTOMER UNTUK MENGIRIM PERMINTAAN KE BACKEND
	addCustomer() {
      //JALANKAN FUNGSI submitCustomer YANG MERUPAKAN ACTIONS DARI MODULE CUSTOMER
      this.submitCustomer().then((res) => {
          //APABILA BERHASIL, MAKA SET DATA CUSTOMER_ID AGAR AUTO SELECT PADA BAGIAN SELECT CUSTOMER
          this.transactions.customer_id = res.data
          this.isForm = false //SET KEMBALI JADI FALSE AGAR FORM TERTUTUP
      })
  }
}

Pindah pada sisi backend, lakukan modifikasi dari method yang meng-handle fungsi untuk menyimpan data customer ke database. Buka file CustomerController.php dan modifikasi method store() menjadi:

public function store(Request $request)
{
    $this->validate($request, [
        'nik' => 'required|string|unique:customers,nik',
        'name' => 'required|string|max:150',
        'address' => 'required|string',
        'phone' => 'required|string|max:15'
    ]);

    $user = $request->user();
    $request->request->add([
        'point' => 0,
        'deposit' => 0
    ]);
    if ($user->role == 3) {
        $request->request->add(['courier_id' => $user->id]);
    }
    $customer = Customer::create($request->all()); //MODIFIKASI BAGIAN INI
    return response()->json(['status' => 'success', 'data' => $customer]); //DAN PASSING DATANYA SEBAGAI RESPONSE
}

Tugas pertama kita telah selesai sampai disini, kamu dapat membuka form add customer dengan menekan tombol new customer. Adapun tampilannya kurang lebih seperti gambar dibawah ini

[Bug] Laundry Product

Sehubungan dengan adanya penambahan informasi product, maka kita perlu untuk menambahkan field baru ke dalam table laundry_prices. Pada command line, jalankan command untuk membuat migration:

php artisan make:migration add_time_to_laundry_prices_table

Buka file migration yang baru saja di-generate dan modifikasi menjadi:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddTimeToLaundryPricesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('laundry_prices', function (Blueprint $table) {
            $table->char('service', 5)->nullable()->after('price');
            $table->string('service_type')->nullable()->after('service');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('laundry_prices', function (Blueprint $table) {
            $table->dropColumn('service');
            $table->dropColumn('service_type');
        });
    }
}

Jangan lupa untuk menerapkan migration tersebut dengan menjalankan command:

php artisan migrate

Selanjutnya buka file ProductController.php dan lakukan modifikasi untuk method store() dan update()

public function store(Request $request)
{
    $this->validate($request, [
        'name' => 'required|string|max:100',
        'unit_type' => 'required',
        'price' => 'required|integer',
        'laundry_type' => 'required',
      
        //TAMBAHKAN VALIDASI UNTUK DUA DATA BARU
        'service' => 'required|integer',
        'service_type' => 'required'
    ]);

    try {
        LaundryPrice::create([
            'name' => $request->name,
            'unit_type' => $request->unit_type,
            'laundry_type_id' => $request->laundry_type,
            'price' => $request->price,
            'user_id' => auth()->user()->id,
          
            //SIMPAN INFORMASI DATA BARU TERSEBUT
            'service' => $request->service,
            'service_type' => $request->service_type
        ]);
        return response()->json(['status' => 'success']);
    } catch (\Exception $e) {
        return response()->json(['status' => 'failed']);
    }
}

public function update(Request $request, $id)
{
    //KARENA BELUM ADA VALIDASI SEBELUMNYA, KITA TAMBAHKAN VALIDASINYA
    $this->validate($request, [
        'name' => 'required|string|max:100',
        'unit_type' => 'required',
        'price' => 'required|integer',
        'laundry_type' => 'required',
        'service' => 'required|integer',
        'service_type' => 'required'
    ]);

    $laundry = LaundryPrice::find($id);
    $laundry->update([
        'name' => $request->name,
        'unit_type' => $request->unit_type,
        'laundry_type_id' => $request->laundry_type,
        'price' => $request->price,
      
        //UPDATE DATA BARU TERSEBUT
        'service' => $request->service,
        'service_type' => $request->service_type
    ]);
    return response()->json(['status' => 'success']);
}

Dari sisi backend sudah selesai, maka saatnya untuk menyesuaikan form tambah dan edit product. Keuntungannya adalah karena form-nya juga re-usable sehingga kita hanya akan meng-edit 1 file untuk menerapkan di kedua tempat. Buka file resources/js/pages/products/Form.vue

<div class="form-group" :class="{ 'has-error': errors.price }">
    <label for="">Harga</label>
    <input type="number" class="form-control" v-model="product.price">
    <p class="text-danger" v-if="errors.price">{{ errors.price[0] }}</p>
</div>

<!-- TAMBAHKAN CODE INI -->
<div class="row">
    <div class="col-md-6">
        <div class="form-group" :class="{ 'has-error': errors.service }">
            <label for="">Lama Pengerjaan</label>
            <input type="number" class="form-control" v-model="product.service">
            <p class="text-danger" v-if="errors.service">{{ errors.service[0] }}</p>
        </div>
    </div>
    <div class="col-md-6">
        <div class="form-group" :class="{ 'has-error': errors.service_type }">
            <label for="">Satuan</label>
            <select class="form-control" v-model="product.service_type">
                <option value="">Pilih</option>
                <option value="Hari">Hari</option>
                <option value="Jam">Jam</option>
            </select>
            <p class="text-danger" v-if="errors.service_type">{{ errors.service_type[0] }}</p>
        </div>
    </div>
</div>
<!-- TAMBAHKAN CODE INI -->

Masih dengan file yang sama, pada bagian property created() dan data() modifikasi menjadi:

created() {
    this.getLaundryType()
    if (this.$route.name == 'products.edit') {
        this.editProduct(this.$route.params.id).then((res) => {
            this.product = {
                name: res.data.name,
                unit_type: res.data.unit_type,
                price: res.data.price,
                laundry_type: res.data.laundry_type_id,
              
                //TAMBAHKAN KEDUA BAGIAN INI
                service: res.data.service,
                service_type: res.data.service_type
            }
        })
    }
},
data() {
    return {
        product: {
            name: '',
            unit_type: '',
            price: '',
            laundry_type: '',
          
            //TAMBAHKAN JUGA KEDUA BAGIAN INI
            service: '',
            service_type: ''
        },
        laundry_type: '',
        showForm: false
    }
},

Sedangkan pada bagian property method() di dalam file yang sama, tambahkan sebuah method baru dan modifikasi method lainnya menjadi

methods: {
  //[.. CODE SEBELUMNYA ..]
  
  //[.. CLEAR FORM KITA BUAT JADI METHOD BARU AGAR REUSABLE ..]
	clearForm() {
      this.product = {
          name: '',
          unit_type: '',
          price: '',
          laundry_type: '',
          service: '',
          service_type: ''
      }
  },
  submit() {
      if (this.$route.name == 'products.add') {
          this.addProductLaundry(this.product).then(() => {
              this.clearForm() //GUNAKAN DISINI
              this.$router.push({ name: 'products.data' })
          })
      } else if (this.$route.name == 'products.edit') {
          Object.assign(this.product, { id: this.$route.params.id })
          this.updateCourier(this.product).then(() => {
              this.clearForm() //GUNAKAN DISINI
              this.$router.push({ name: 'products.data' })
          })
      }
  }
}

Edit dan Tambah product untuk item laundry sudah selesai dan dapat digunakan sebagaimana mestinya. Pendekatan terakhir adalah menampilkan informasi baru tersebut pada list product. Buka file resources/js/pages/Product.vue dan modifikasi beberapa bagian berikut:

<!-- [.. CODE SEBELUMNYA ..] -->

<b-table striped hover bordered :items="products.data" :fields="fields" show-empty>
    <template slot="laundry_type" slot-scope="row">
        {{ row.item.type.name }}
    </template>
    <template slot="user_id" slot-scope="row">
        {{ row.item.user.name }}
    </template>
    
    <!-- TAMBAHKAN CODE INI -->
    <template slot="service" slot-scope="row">
        {{ row.item.service }} {{ row.item.service_type }}
    </template>
    <!-- TAMBAHKAN CODE INI -->
    
    <template slot="actions" slot-scope="row">
        <router-link :to="{ name: 'products.edit', params: {id: row.item.id} }" class="btn btn-warning btn-sm"><i class="fa fa-pencil"></i></router-link>
        <button class="btn btn-danger btn-sm" @click="deleteProduct(row.item.id)"><i class="fa fa-trash"></i></button>
    </template>
</b-table>

<!-- [.. CODE SETELAHNYA ..] -->

Terakhir pada bagian property data() di dalam file yang sama, modifikasi variable fields menjadi

fields: [
    { key: 'name', label: 'Nama Item' },
    { key: 'unit_type', label: 'Tipe' },
    { key: 'laundry_type', label: 'Jenis Jasa' },
    { key: 'price', label: 'Harga' },
    { key: 'user_id', label: 'Admin' },
    { key: 'service', label: 'Lama Pengerjaan' }, //TAMBAHKAN BAGIAN INI
    { key: 'actions', label: 'Aksi' }
],

Jangan lupa untuk menjalankan command npm run dev untuk men-compile perubahan yang telah dilakukan.

Baca Juga: Aplikasi Laundry (Laravel 5.8 - Vue.js - SPA) #8: Manage Customers

Kesimpulan

Artikel ini telah mengajarkan kita bagaimana cara menyelesaikan sebuah fitur baru dan memperbaiki bug yang ditemukan ketika proses development berjalan. Sehingga dapat disimpulkan bahwa membuat program hanyalah menyatukan potongan-potongan fitur yang saling terkait.

Sebenarnya artikel ini masih panjang, akan tetapi saya menemukan bug baru lagi dimana schema yang kita kerjakan adalah dalam satu invoice bisa lebih dari 1 layanan yang digunakan. Sehingga start_date dan end_date harusnya berada pada table detail_transactions sehingga jelas informasi masing-masing layanan kapan waktu pengerjaan dan selesai dikerjakan.

Agar tidak bingung maka artikel ini saya akhir sampai disini dan penyelesaian bug-nya akan dilanjutkan pada artikel selanjutnya. Adapun dokumentasi code dari artikel ini dapat kamu lihat di Github.

Category:
Share:

Comments