Browse Source
Added Product Management feature
Added Product Management feature
Set product credit_price attribute to nullable Added master entity lang filepull/4/head
10 changed files with 402 additions and 4 deletions
-
73app/Http/Controllers/ProductsController.php
-
3app/Product.php
-
2database/migrations/2017_04_09_013901_create_products_table.php
-
28resources/lang/id/master.php
-
30resources/lang/id/product.php
-
5resources/views/layouts/partials/top-nav.blade.php
-
50resources/views/products/index.blade.php
-
47resources/views/products/partials/forms.blade.php
-
14routes/web.php
-
154tests/Feature/ManageProductsTest.php
@ -0,0 +1,73 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Controllers; |
|||
|
|||
use App\Product; |
|||
use Illuminate\Http\Request; |
|||
|
|||
class ProductsController extends Controller |
|||
{ |
|||
public function index(Request $request) |
|||
{ |
|||
$editableProduct = null; |
|||
$q = $request->get('q'); |
|||
$products = Product::where(function($query) use ($q) { |
|||
if ($q) { |
|||
$query->where('name', 'like', '%' . $q . '%'); |
|||
} |
|||
}) |
|||
->orderBy('name')->paginate(25); |
|||
|
|||
if (in_array($request->get('action'), ['edit','delete']) && $request->has('id')) |
|||
$editableProduct = Product::find($request->get('id')); |
|||
|
|||
return view('products.index', compact('products','editableProduct')); |
|||
} |
|||
|
|||
public function store(Request $request) |
|||
{ |
|||
$this->validate($request, [ |
|||
'name' => 'required|max:20', |
|||
'cash_price' => 'required|numeric', |
|||
'credit_price' => 'nullable|numeric', |
|||
]); |
|||
|
|||
Product::create($request->only('name','cash_price','credit_price')); |
|||
|
|||
flash(trans('product.created'), 'success'); |
|||
|
|||
return redirect()->route('products.index'); |
|||
} |
|||
|
|||
public function update(Request $request, $productId) |
|||
{ |
|||
$this->validate($request, [ |
|||
'name' => 'required|max:20', |
|||
'cash_price' => 'required|numeric', |
|||
'credit_price' => 'nullable|numeric', |
|||
]); |
|||
|
|||
$routeParam = $request->only('q'); |
|||
|
|||
$product = Product::findOrFail($productId)->update($request->only('name','cash_price','credit_price')); |
|||
|
|||
flash(trans('product.updated'), 'success'); |
|||
|
|||
return redirect()->route('products.index', $routeParam); |
|||
} |
|||
|
|||
public function destroy(Request $request, $productId) |
|||
{ |
|||
$this->validate($request, [ |
|||
'product_id' => 'required|exists:products,id', |
|||
]); |
|||
|
|||
if ($request->get('product_id') == $productId && Product::findOrFail($productId)->delete()) { |
|||
flash(trans('product.deleted'), 'success'); |
|||
return redirect()->route('products.index'); |
|||
} |
|||
|
|||
flash(trans('product.undeleted'), 'error'); |
|||
return back(); |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
<?php |
|||
|
|||
return [ |
|||
// Labels
|
|||
'master' => 'Master', |
|||
'list' => 'Daftar Master', |
|||
'search' => 'Cari Master', |
|||
'not_found' => 'Master tidak ditemukan', |
|||
'empty' => 'Belum ada Master', |
|||
'back_to_index' => 'Kembali ke daftar Master', |
|||
|
|||
// Actions
|
|||
'create' => 'Input Master Baru', |
|||
'created' => 'Input Master baru telah berhasil.', |
|||
'show' => 'Detail Master', |
|||
'edit' => 'Edit Master', |
|||
'update' => 'Update Master', |
|||
'updated' => 'Update data Master telah berhasil.', |
|||
'delete' => 'Hapus Master', |
|||
'delete_confirm' => 'Anda yakin akan menghapus Master ini?', |
|||
'deleted' => 'Hapus data Master telah berhasil.', |
|||
'undeleted' => 'Data Master gagal dihapus.', |
|||
'undeleteable' => 'Data Master tidak dapat dihapus.', |
|||
|
|||
// Attributes
|
|||
'name' => 'Nama Master', |
|||
'description' => 'Deskripsi Master', |
|||
]; |
|||
@ -0,0 +1,30 @@ |
|||
<?php |
|||
|
|||
return [ |
|||
// Labels
|
|||
'master' => 'Produk', |
|||
'list' => 'Daftar Produk', |
|||
'search' => 'Cari Produk', |
|||
'not_found' => 'Produk tidak ditemukan', |
|||
'empty' => 'Belum ada Produk', |
|||
'price' => 'Harga', |
|||
'back_to_index' => 'Kembali ke daftar Produk', |
|||
|
|||
// Actions
|
|||
'create' => 'Input Produk Baru', |
|||
'created' => 'Input Produk baru telah berhasil.', |
|||
'show' => 'Detail Produk', |
|||
'edit' => 'Edit Produk', |
|||
'update' => 'Update Produk', |
|||
'updated' => 'Update data Produk telah berhasil.', |
|||
'delete' => 'Hapus Produk', |
|||
'delete_confirm' => 'Anda yakin akan menghapus Produk ini?', |
|||
'deleted' => 'Hapus data Produk telah berhasil.', |
|||
'undeleted' => 'Data Produk gagal dihapus.', |
|||
'undeleteable' => 'Data Produk tidak dapat dihapus.', |
|||
|
|||
// Attributes
|
|||
'name' => 'Nama Produk', |
|||
'cash_price' => 'Harga Tunai', |
|||
'credit_price' => 'Harga Kredit', |
|||
]; |
|||
@ -0,0 +1,50 @@ |
|||
@extends('layouts.app') |
|||
|
|||
@section('title', trans('product.list')) |
|||
|
|||
@section('content') |
|||
<h3 class="page.header">{{ trans('product.list') }}</h3> |
|||
|
|||
<div class="row"> |
|||
<div class="col-md-8"> |
|||
<div class="panel panel-default table-responsive"> |
|||
<div class="panel-heading"> |
|||
{{ Form::open(['method' => 'get','class' => 'form-inline']) }} |
|||
{!! FormField::text('q', ['value' => request('q'), 'label' => trans('product.search'), 'class' => 'input-sm']) !!} |
|||
{{ Form::submit(trans('product.search'), ['class' => 'btn btn-sm']) }} |
|||
{{ link_to_route('products.index', trans('app.reset')) }} |
|||
{{ Form::close() }} |
|||
</div> |
|||
<table class="table table-condensed"> |
|||
<thead> |
|||
<tr> |
|||
<th class="text-center">{{ trans('app.table_no') }}</th> |
|||
<th>{{ trans('product.name') }}</th> |
|||
<th class="text-right">{{ trans('product.cash_price') }}</th> |
|||
<th class="text-right">{{ trans('product.credit_price') }}</th> |
|||
<th class="text-center">{{ trans('app.action') }}</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<?php $customersTotal = 0 ?>
|
|||
@foreach($products as $key => $product) |
|||
<tr> |
|||
<td class="text-center">{{ $products->firstItem() + $key }}</td> |
|||
<td>{{ $product->name }}</td> |
|||
<td class="text-right">{{ formatRp($product->cash_price) }}</td> |
|||
<td class="text-right">{{ formatRp($product->credit_price) }}</td> |
|||
<td class="text-center"> |
|||
{!! link_to_route('products.index', trans('app.edit'), ['action' => 'edit', 'id' => $product->id] + Request::only('q'), ['id' => 'edit-product-' . $product->id]) !!} | |
|||
{!! link_to_route('products.index', trans('app.delete'), ['action' => 'delete', 'id' => $product->id] + Request::only('q'), ['id' => 'del-product-' . $product->id]) !!} |
|||
</td> |
|||
</tr> |
|||
@endforeach |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4"> |
|||
@include('products.partials.forms') |
|||
</div> |
|||
</div> |
|||
@endsection |
|||
@ -0,0 +1,47 @@ |
|||
@if (! Request::has('action')) |
|||
{{ link_to_route('products.index', trans('product.create'), ['action' => 'create'], ['class' => 'btn btn-success pull-right']) }} |
|||
@endif |
|||
@if (Request::get('action') == 'create') |
|||
{!! Form::open(['route' => 'products.store']) !!} |
|||
{!! FormField::text('name', ['label' => trans('product.name'), 'required' => true]) !!} |
|||
<div class="row"> |
|||
<div class="col-md-6">{!! FormField::price('cash_price', ['label' => trans('product.cash_price'), 'required' => true]) !!}</div> |
|||
<div class="col-md-6">{!! FormField::price('credit_price', ['label' => trans('product.credit_price')]) !!}</div> |
|||
</div> |
|||
{!! Form::submit(trans('product.create'), ['class' => 'btn btn-success']) !!} |
|||
{!! Form::hidden('cat', 'product') !!} |
|||
{{ link_to_route('products.index', trans('app.cancel'), [], ['class' => 'btn btn-default']) }} |
|||
{!! Form::close() !!} |
|||
@endif |
|||
@if (Request::get('action') == 'edit' && $editableProduct) |
|||
{!! Form::model($editableProduct, ['route' => ['products.update', $editableProduct->id],'method' => 'patch']) !!} |
|||
{!! FormField::text('name', ['label' => trans('product.name'), 'required' => true]) !!} |
|||
<div class="row"> |
|||
<div class="col-md-6">{!! FormField::price('cash_price', ['label' => trans('product.cash_price'), 'required' => true]) !!}</div> |
|||
<div class="col-md-6">{!! FormField::price('credit_price', ['label' => trans('product.credit_price')]) !!}</div> |
|||
</div> |
|||
{{ Form::hidden('q', request('q')) }} |
|||
{!! Form::submit(trans('product.update'), ['class' => 'btn btn-success']) !!} |
|||
{{ link_to_route('products.index', trans('app.cancel'), Request::only('q'), ['class' => 'btn btn-default']) }} |
|||
{!! Form::close() !!} |
|||
@endif |
|||
@if (Request::get('action') == 'delete' && $editableProduct) |
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading"><h3 class="panel-title">{{ trans('product.delete') }}</h3></div> |
|||
<div class="panel-body"> |
|||
<table class="table table-condensed"> |
|||
<tbody> |
|||
<tr><th>{{ trans('product.name') }}</th><td>{{ $editableProduct->name }}</td></tr> |
|||
<tr><th>{{ trans('product.cash_price') }}</th><td>{{ formatRp($editableProduct->cash_price) }}</td></tr> |
|||
<tr><th>{{ trans('product.credit_price') }}</th><td>{{ formatRp($editableProduct->credit_price) }}</td></tr> |
|||
</tbody> |
|||
</table> |
|||
<hr> |
|||
{{ trans('product.delete_confirm') }} |
|||
</div> |
|||
<div class="panel-footer"> |
|||
{!! FormField::delete(['route'=>['products.destroy',$editableProduct->id]], trans('app.delete_confirm_button'), ['class'=>'btn btn-danger'], ['product_id'=>$editableProduct->id]) !!} |
|||
{{ link_to_route('products.index', trans('app.cancel'), Request::only('q'), ['class' => 'btn btn-default']) }} |
|||
</div> |
|||
</div> |
|||
@endif |
|||
@ -0,0 +1,154 @@ |
|||
<?php |
|||
|
|||
namespace Tests\Feature; |
|||
|
|||
use App\Product; |
|||
use Illuminate\Foundation\Testing\DatabaseMigrations; |
|||
use Tests\BrowserKitTestCase; |
|||
|
|||
class ManageProductsTest extends BrowserKitTestCase |
|||
{ |
|||
use DatabaseMigrations; |
|||
|
|||
/** @test */ |
|||
public function user_can_see_paginated_product_list_in_product_index_page() |
|||
{ |
|||
$product1 = factory(Product::class)->create(['name' => 'Testing 123']); |
|||
$product2 = factory(Product::class)->create(['name' => 'Testing 456']); |
|||
|
|||
$this->loginAsUser(); |
|||
$this->visit(route('products.index')); |
|||
$this->see($product1->name); |
|||
$this->see($product2->name); |
|||
} |
|||
|
|||
/** @test */ |
|||
public function user_can_search_product_by_keyword() |
|||
{ |
|||
$this->loginAsUser(); |
|||
$product1 = factory(Product::class)->create(['name' => 'Testing 123']); |
|||
$product2 = factory(Product::class)->create(['name' => 'Testing 456']); |
|||
|
|||
$this->visit(route('products.index')); |
|||
$this->submitForm(trans('product.search'), ['q' => '123']); |
|||
$this->seePageIs(route('products.index', ['q' => 123])); |
|||
|
|||
$this->see($product1->name); |
|||
$this->dontSee($product2->name); |
|||
} |
|||
|
|||
/** @test */ |
|||
public function user_can_create_a_product() |
|||
{ |
|||
$this->loginAsUser(); |
|||
$this->visit(route('products.index')); |
|||
|
|||
$this->click(trans('product.create')); |
|||
$this->seePageIs(route('products.index', ['action' => 'create'])); |
|||
|
|||
$this->type('Product 1', 'name'); |
|||
$this->type('1000', 'cash_price'); |
|||
$this->type('1200', 'credit_price'); |
|||
$this->press(trans('product.create')); |
|||
|
|||
$this->seePageIs(route('products.index')); |
|||
$this->see(trans('product.created')); |
|||
|
|||
$this->seeInDatabase('products', [ |
|||
'name' => 'Product 1', |
|||
'cash_price' => 1000, |
|||
'credit_price' => 1200, |
|||
]); |
|||
} |
|||
|
|||
/** @test */ |
|||
public function user_can_edit_a_product_within_search_query() |
|||
{ |
|||
$this->loginAsUser(); |
|||
$product = factory(Product::class)->create(['name' => 'Testing 123']); |
|||
|
|||
$this->visit(route('products.index', ['q' => '123'])); |
|||
$this->click('edit-product-' . $product->id); |
|||
$this->seePageIs(route('products.index', ['action' => 'edit','id' => $product->id, 'q' => '123'])); |
|||
|
|||
$this->type('Product 1', 'name'); |
|||
$this->type('1000', 'cash_price'); |
|||
$this->type('1200', 'credit_price'); |
|||
$this->press(trans('product.update')); |
|||
|
|||
$this->seePageIs(route('products.index', ['q' => '123'])); |
|||
|
|||
$this->seeInDatabase('products', [ |
|||
'name' => 'Product 1', |
|||
'cash_price' => 1000, |
|||
'credit_price' => 1200, |
|||
]); |
|||
} |
|||
|
|||
/** @test */ |
|||
public function user_can_create_a_product_with_only_cash_price() |
|||
{ |
|||
$this->loginAsUser(); |
|||
$this->visit(route('products.index')); |
|||
|
|||
$this->click(trans('product.create')); |
|||
$this->seePageIs(route('products.index', ['action' => 'create'])); |
|||
|
|||
$this->type('Product 1', 'name'); |
|||
$this->type('1000', 'cash_price'); |
|||
$this->type('', 'credit_price'); |
|||
$this->press(trans('product.create')); |
|||
|
|||
$this->seePageIs(route('products.index')); |
|||
$this->see(trans('product.created')); |
|||
|
|||
$this->seeInDatabase('products', [ |
|||
'name' => 'Product 1', |
|||
'cash_price' => 1000, |
|||
'credit_price' => null, |
|||
]); |
|||
} |
|||
|
|||
/** @test */ |
|||
public function user_can_edit_a_product() |
|||
{ |
|||
$this->loginAsUser(); |
|||
$product = factory(Product::class)->create(); |
|||
|
|||
$this->visit(route('products.index')); |
|||
$this->click('edit-product-' . $product->id); |
|||
$this->seePageIs(route('products.index', ['action' => 'edit','id' => $product->id])); |
|||
|
|||
$this->type('Product 1', 'name'); |
|||
$this->type('1000', 'cash_price'); |
|||
$this->type('1200', 'credit_price'); |
|||
$this->press(trans('product.update')); |
|||
|
|||
$this->seeInDatabase('products', [ |
|||
'name' => 'Product 1', |
|||
'cash_price' => 1000, |
|||
'credit_price' => 1200, |
|||
]); |
|||
} |
|||
|
|||
/** @test */ |
|||
public function user_can_delete_a_product() |
|||
{ |
|||
$this->loginAsUser(); |
|||
$product = factory(Product::class)->create(); |
|||
|
|||
$this->visit(route('products.index')); |
|||
$this->click('del-product-' . $product->id); |
|||
$this->seePageIs(route('products.index', ['action' => 'delete','id' => $product->id])); |
|||
|
|||
$this->seeInDatabase('products', [ |
|||
'id' => $product->id |
|||
]); |
|||
|
|||
$this->press(trans('app.delete_confirm_button')); |
|||
|
|||
$this->dontSeeInDatabase('products', [ |
|||
'id' => $product->id |
|||
]); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue