Browse Source

Added Product Management feature

Set product credit_price attribute to nullable
Added master entity lang file
pull/4/head
Nafies Luthfi 9 years ago
parent
commit
a9c662ce23
  1. 73
      app/Http/Controllers/ProductsController.php
  2. 3
      app/Product.php
  3. 2
      database/migrations/2017_04_09_013901_create_products_table.php
  4. 28
      resources/lang/id/master.php
  5. 30
      resources/lang/id/product.php
  6. 5
      resources/views/layouts/partials/top-nav.blade.php
  7. 50
      resources/views/products/index.blade.php
  8. 47
      resources/views/products/partials/forms.blade.php
  9. 14
      routes/web.php
  10. 154
      tests/Feature/ManageProductsTest.php

73
app/Http/Controllers/ProductsController.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();
}
}

3
app/Product.php

@ -6,10 +6,11 @@ use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $fillable = ['cash_price', 'credit_price'];
protected $fillable = ['name','cash_price','credit_price'];
public function getPrice($type = 'cash')
{
// TODO: if there is no credit_price then return cash_price
if ($type == 'credit') {
return $this->credit_price;
}

2
database/migrations/2017_04_09_013901_create_products_table.php

@ -17,7 +17,7 @@ class CreateProductsTable extends Migration
$table->increments('id');
$table->string('name');
$table->unsignedInteger('cash_price');
$table->unsignedInteger('credit_price');
$table->unsignedInteger('credit_price')->nullable();
$table->timestamps();
});
}

28
resources/lang/id/master.php

@ -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',
];

30
resources/lang/id/product.php

@ -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',
];

5
resources/views/layouts/partials/top-nav.blade.php

@ -22,12 +22,15 @@
<!-- Authentication Links -->
@if (Auth::check())
<li>
<form class="" action="{{ route('cart.add') }}" method="POST">
<form class="" style="padding-left: 10px;" action="{{ route('cart.add') }}" method="POST">
{{ csrf_field() }}
<input type="submit" class="btn btn-default navbar-btn" name="create-cash-draft" id="cash-draft-create-button" value="{{ trans('transaction.create_cash') }}">
<input type="submit" class="btn btn-default navbar-btn" name="create-credit-draft" id="credit-draft-create-button" value="{{ trans('transaction.create_credit') }}">
</form>
</li>
<li>
{{ link_to_route('products.index', trans('product.list')) }}
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
{{ Auth::user()->name }} <span class="caret"></span>

50
resources/views/products/index.blade.php

@ -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

47
resources/views/products/partials/forms.blade.php

@ -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

14
routes/web.php

@ -17,8 +17,15 @@ Route::get('/', function () {
Auth::routes();
Route::get('/home', 'CartController@index')->name('home');
Route::group(['middleware' => 'auth'], function () {
/**
* Pages Routes
*/
Route::get('/home', 'CartController@index')->name('home');
/**
* Cart / Trasanction Draft Routes
*/
Route::get('drafts', 'CartController@index')->name('cart.index');
Route::get('drafts/{draftKey}', 'CartController@show')->name('cart.show');
Route::post('drafts/{draftKey}', 'CartController@store')->name('cart.store');
@ -30,4 +37,9 @@ Route::group(['middleware' => 'auth'], function () {
Route::delete('cart/empty/{draftKey}', 'CartController@empty')->name('cart.empty');
Route::delete('cart/remove', 'CartController@remove')->name('cart.remove');
Route::delete('cart/destroy', 'CartController@destroy')->name('cart.destroy');
/**
* Products Routes
*/
Route::resource('products', 'ProductsController');
});

154
tests/Feature/ManageProductsTest.php

@ -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
]);
}
}
Loading…
Cancel
Save