Browse Source

Added ProductUnit Management feature and add unit_id to product attribute

pull/4/head
Nafies Luthfi 9 years ago
parent
commit
cbb8b43138
  1. 5
      app/Http/Controllers/ProductsController.php
  2. 63
      app/Http/Controllers/UnitsController.php
  3. 7
      app/Product.php
  4. 6
      app/Providers/AppServiceProvider.php
  5. 11
      app/Unit.php
  6. 21
      database/factories/ModelFactory.php
  7. 3
      database/migrations/2017_04_09_013901_create_products_table.php
  8. 32
      database/migrations/2017_05_02_211915_create_product_units_table.php
  9. 5
      resources/views/layouts/partials/top-nav.blade.php
  10. 4
      resources/views/products/partials/forms.blade.php
  11. 35
      resources/views/units/index.blade.php
  12. 33
      resources/views/units/partials/forms.blade.php
  13. 7
      routes/web.php
  14. 9
      tests/Feature/ManageProductsTest.php
  15. 108
      tests/Feature/ManageUnitsTest.php

5
app/Http/Controllers/ProductsController.php

@ -30,9 +30,10 @@ class ProductsController extends Controller
'name' => 'required|max:20', 'name' => 'required|max:20',
'cash_price' => 'required|numeric', 'cash_price' => 'required|numeric',
'credit_price' => 'nullable|numeric', 'credit_price' => 'nullable|numeric',
'unit_id' => 'required|numeric',
]); ]);
Product::create($request->only('name','cash_price','credit_price'));
Product::create($request->only('name','cash_price','credit_price','unit_id'));
flash(trans('product.created'), 'success'); flash(trans('product.created'), 'success');
@ -49,7 +50,7 @@ class ProductsController extends Controller
$routeParam = $request->only('q'); $routeParam = $request->only('q');
$product = Product::findOrFail($productId)->update($request->only('name','cash_price','credit_price'));
$product = Product::findOrFail($productId)->update($request->only('name','cash_price','credit_price','unit_id'));
flash(trans('product.updated'), 'success'); flash(trans('product.updated'), 'success');

63
app/Http/Controllers/UnitsController.php

@ -0,0 +1,63 @@
<?php
namespace App\Http\Controllers;
use App\Unit;
use Illuminate\Http\Request;
class UnitsController extends Controller
{
public function index(Request $request)
{
$editableUnit = null;
$units = Unit::all();
if (in_array($request->get('action'), ['edit','delete']) && $request->has('id'))
$editableUnit = Unit::find($request->get('id'));
return view('units.index', compact('units','editableUnit'));
}
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|max:20',
]);
Unit::create($request->only('name'));
flash(trans('unit.created'), 'success');
return redirect()->route('units.index');
}
public function update(Request $request, $unitId)
{
$this->validate($request, [
'name' => 'required|max:20',
]);
$unit = Unit::findOrFail($unitId)->update($request->only('name'));
flash(trans('unit.updated'), 'success');
return redirect()->route('units.index');
}
public function destroy(Request $request, Unit $unit)
{
$this->validate($request, [
'unit_id' => 'required|exists:product_units,id|not_exists:products,unit_id',
], [
'unit_id.not_exists' => trans('unit.undeleted')
]);
if ($request->get('unit_id') == $unit->id && $unit->delete()) {
flash(trans('unit.deleted'), 'success');
return redirect()->route('units.index');
}
flash(trans('unit.undeleted'), 'error');
return back();
}
}

7
app/Product.php

@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
class Product extends Model class Product extends Model
{ {
protected $fillable = ['name','cash_price','credit_price'];
protected $fillable = ['name','cash_price','credit_price','unit_id'];
public function getPrice($type = 'cash') public function getPrice($type = 'cash')
{ {
@ -17,4 +17,9 @@ class Product extends Model
return $this->cash_price; return $this->cash_price;
} }
public function unit()
{
return $this->belongsTo(Unit::class);
}
} }

6
app/Providers/AppServiceProvider.php

@ -14,6 +14,12 @@ class AppServiceProvider extends ServiceProvider
public function boot() public function boot()
{ {
require_once app_path().'/Helpers/helpers.php'; require_once app_path().'/Helpers/helpers.php';
\Validator::extend('not_exists', function($attribute, $value, $parameters)
{
return \DB::table($parameters[0])
->where($parameters[1], $value)
->count() < 1;
});
} }
/** /**

11
app/Unit.php

@ -0,0 +1,11 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Unit extends Model
{
protected $table = 'product_units';
protected $fillable = ['name'];
}

21
database/factories/ModelFactory.php

@ -1,16 +1,5 @@
<?php <?php
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| Here you may define all of your model factories. Model factories give
| you a convenient way to create models for testing and seeding your
| database. Just tell the factory how a default model should look.
|
*/
/** @var \Illuminate\Database\Eloquent\Factory $factory */ /** @var \Illuminate\Database\Eloquent\Factory $factory */
$factory->define(App\User::class, function (Faker\Generator $faker) { $factory->define(App\User::class, function (Faker\Generator $faker) {
return [ return [
@ -25,7 +14,17 @@ $factory->define(App\User::class, function (Faker\Generator $faker) {
$factory->define(App\Product::class, function (Faker\Generator $faker) { $factory->define(App\Product::class, function (Faker\Generator $faker) {
return [ return [
'name' => $faker->name, 'name' => $faker->name,
'unit_id' => function() {
return factory(App\Unit::class)->create()->id;
},
'cash_price' => 2000, 'cash_price' => 2000,
'credit_price' => 1000, 'credit_price' => 1000,
]; ];
}); });
/* @var \Illuminate\Database\Eloquent\Factory $factory */
$factory->define(App\Unit::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
];
});

3
database/migrations/2017_04_09_013901_create_products_table.php

@ -15,7 +15,8 @@ class CreateProductsTable extends Migration
{ {
Schema::create('products', function (Blueprint $table) { Schema::create('products', function (Blueprint $table) {
$table->increments('id'); $table->increments('id');
$table->string('name');
$table->string('name', 60);
$table->unsignedInteger('unit_id');
$table->unsignedInteger('cash_price'); $table->unsignedInteger('cash_price');
$table->unsignedInteger('credit_price')->nullable(); $table->unsignedInteger('credit_price')->nullable();
$table->timestamps(); $table->timestamps();

32
database/migrations/2017_05_02_211915_create_product_units_table.php

@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductUnitsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('product_units', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 20);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('product_units');
}
}

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

@ -28,9 +28,8 @@
<input type="submit" class="btn btn-default navbar-btn" name="create-credit-draft" id="credit-draft-create-button" value="{{ trans('transaction.create_credit') }}"> <input type="submit" class="btn btn-default navbar-btn" name="create-credit-draft" id="credit-draft-create-button" value="{{ trans('transaction.create_credit') }}">
</form> </form>
</li> </li>
<li>
{{ link_to_route('products.index', trans('product.list')) }}
</li>
<li>{{ link_to_route('products.index', trans('product.list')) }}</li>
<li>{{ link_to_route('units.index', trans('unit.list')) }}</li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
{{ Auth::user()->name }} <span class="caret"></span> {{ Auth::user()->name }} <span class="caret"></span>

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

@ -1,3 +1,4 @@
@inject('unit', 'App\Unit')
@if (! Request::has('action')) @if (! Request::has('action'))
{{ link_to_route('products.index', trans('product.create'), ['action' => 'create'], ['class' => 'btn btn-success pull-right']) }} {{ link_to_route('products.index', trans('product.create'), ['action' => 'create'], ['class' => 'btn btn-success pull-right']) }}
@endif @endif
@ -8,6 +9,7 @@
<div class="col-md-6">{!! FormField::price('cash_price', ['label' => trans('product.cash_price'), 'required' => true]) !!}</div> <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 class="col-md-6">{!! FormField::price('credit_price', ['label' => trans('product.credit_price')]) !!}</div>
</div> </div>
{!! FormField::select('unit_id', $unit->pluck('name','id'), ['label' => trans('product.unit'), 'required' => true]) !!}
{!! Form::submit(trans('product.create'), ['class' => 'btn btn-success']) !!} {!! Form::submit(trans('product.create'), ['class' => 'btn btn-success']) !!}
{!! Form::hidden('cat', 'product') !!} {!! Form::hidden('cat', 'product') !!}
{{ link_to_route('products.index', trans('app.cancel'), [], ['class' => 'btn btn-default']) }} {{ link_to_route('products.index', trans('app.cancel'), [], ['class' => 'btn btn-default']) }}
@ -20,6 +22,7 @@
<div class="col-md-6">{!! FormField::price('cash_price', ['label' => trans('product.cash_price'), 'required' => true]) !!}</div> <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 class="col-md-6">{!! FormField::price('credit_price', ['label' => trans('product.credit_price')]) !!}</div>
</div> </div>
{!! FormField::select('unit_id', $unit->pluck('name','id'), ['label' => trans('product.unit'), 'required' => true]) !!}
@if (request('q')) @if (request('q'))
{{ Form::hidden('q', request('q')) }} {{ Form::hidden('q', request('q')) }}
@endif @endif
@ -34,6 +37,7 @@
<table class="table table-condensed"> <table class="table table-condensed">
<tbody> <tbody>
<tr><th>{{ trans('product.name') }}</th><td>{{ $editableProduct->name }}</td></tr> <tr><th>{{ trans('product.name') }}</th><td>{{ $editableProduct->name }}</td></tr>
<tr><th>{{ trans('product.unit') }}</th><td>{{ $editableProduct->unit->name }}</td></tr>
<tr><th>{{ trans('product.cash_price') }}</th><td>{{ formatRp($editableProduct->cash_price) }}</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> <tr><th>{{ trans('product.credit_price') }}</th><td>{{ formatRp($editableProduct->credit_price) }}</td></tr>
</tbody> </tbody>

35
resources/views/units/index.blade.php

@ -0,0 +1,35 @@
@extends('layouts.app')
@section('title', trans('unit.list'))
@section('content')
<div class="row">
<div class="col-md-8">
<div class="panel panel-default table-responsive">
<table class="table table-condensed">
<thead>
<tr>
<th class="text-center">{{ trans('app.table_no') }}</th>
<th>{{ trans('unit.name') }}</th>
<th class="text-center">{{ trans('app.action') }}</th>
</tr>
</thead>
<tbody>
@foreach($units as $key => $unit)
<tr>
<td class="text-center">{{ 1 + $key }}</td>
<td>{{ $unit->name }}</td>
<td class="text-center">
{!! link_to_route('units.index', trans('app.edit'), ['action' => 'edit', 'id' => $unit->id], ['id' => 'edit-unit-' . $unit->id]) !!} |
{!! link_to_route('units.index', trans('app.delete'), ['action' => 'delete', 'id' => $unit->id], ['id' => 'del-unit-' . $unit->id]) !!}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<div class="col-md-4">@include('units.partials.forms')</div>
</div>
@endsection

33
resources/views/units/partials/forms.blade.php

@ -0,0 +1,33 @@
@if (! Request::has('action'))
{{ link_to_route('units.index', trans('unit.create'), ['action' => 'create'], ['class' => 'btn btn-success pull-right']) }}
@endif
@if (Request::get('action') == 'create')
{!! Form::open(['route' => 'units.store']) !!}
{!! FormField::text('name') !!}
{!! Form::submit(trans('unit.create'), ['class' => 'btn btn-success']) !!}
{{ link_to_route('units.index', trans('app.cancel'), [], ['class' => 'btn btn-default']) }}
{!! Form::close() !!}
@endif
@if (Request::get('action') == 'edit' && $editableUnit)
{!! Form::model($editableUnit, ['route' => ['units.update', $editableUnit->id],'method' => 'patch']) !!}
{!! FormField::text('name') !!}
{!! Form::submit(trans('unit.update'), ['class' => 'btn btn-success']) !!}
{{ link_to_route('units.index', trans('app.cancel'), [], ['class' => 'btn btn-default']) }}
{!! Form::close() !!}
@endif
@if (Request::get('action') == 'delete' && $editableUnit)
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{ trans('unit.delete') }}</h3></div>
<div class="panel-body">
<label class="control-label">{{ trans('unit.name') }}</label>
<p>{{ $editableUnit->name }}</p>
{!! $errors->first('unit_id', '<span class="form-error small">:message</span>') !!}
</div>
<hr style="margin:0">
<div class="panel-body">{{ trans('app.delete_confirm') }}</div>
<div class="panel-footer">
{!! FormField::delete(['route'=>['units.destroy',$editableUnit->id]], trans('app.delete_confirm_button'), ['class'=>'btn btn-danger'], ['unit_id'=>$editableUnit->id]) !!}
{{ link_to_route('units.index', trans('app.cancel'), [], ['class' => 'btn btn-default']) }}
</div>
</div>
@endif

7
routes/web.php

@ -41,5 +41,10 @@ Route::group(['middleware' => 'auth'], function () {
/** /**
* Products Routes * Products Routes
*/ */
Route::resource('products', 'ProductsController');
Route::resource('products', 'ProductsController', ['except' => ['create','show','edit']]);
/**
* Units Routes
*/
Route::resource('units', 'UnitsController', ['except' => ['create','show','edit']]);
}); });

9
tests/Feature/ManageProductsTest.php

@ -3,6 +3,7 @@
namespace Tests\Feature; namespace Tests\Feature;
use App\Product; use App\Product;
use App\Unit;
use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseMigrations;
use Tests\BrowserKitTestCase; use Tests\BrowserKitTestCase;
@ -40,6 +41,7 @@ class ManageProductsTest extends BrowserKitTestCase
/** @test */ /** @test */
public function user_can_create_a_product() public function user_can_create_a_product()
{ {
$unit = factory(Unit::class)->create(['name' => 'Testing 123']);
$this->loginAsUser(); $this->loginAsUser();
$this->visit(route('products.index')); $this->visit(route('products.index'));
@ -49,6 +51,7 @@ class ManageProductsTest extends BrowserKitTestCase
$this->type('Product 1', 'name'); $this->type('Product 1', 'name');
$this->type('1000', 'cash_price'); $this->type('1000', 'cash_price');
$this->type('1200', 'credit_price'); $this->type('1200', 'credit_price');
$this->type($unit->id, 'unit_id');
$this->press(trans('product.create')); $this->press(trans('product.create'));
$this->seePageIs(route('products.index')); $this->seePageIs(route('products.index'));
@ -64,6 +67,7 @@ class ManageProductsTest extends BrowserKitTestCase
/** @test */ /** @test */
public function user_can_edit_a_product_within_search_query() public function user_can_edit_a_product_within_search_query()
{ {
$unit = factory(Unit::class)->create(['name' => 'Testing 123']);
$this->loginAsUser(); $this->loginAsUser();
$product = factory(Product::class)->create(['name' => 'Testing 123']); $product = factory(Product::class)->create(['name' => 'Testing 123']);
@ -74,6 +78,7 @@ class ManageProductsTest extends BrowserKitTestCase
$this->type('Product 1', 'name'); $this->type('Product 1', 'name');
$this->type('1000', 'cash_price'); $this->type('1000', 'cash_price');
$this->type('1200', 'credit_price'); $this->type('1200', 'credit_price');
$this->type($unit->id, 'unit_id');
$this->press(trans('product.update')); $this->press(trans('product.update'));
$this->seePageIs(route('products.index', ['q' => '123'])); $this->seePageIs(route('products.index', ['q' => '123']));
@ -88,6 +93,7 @@ class ManageProductsTest extends BrowserKitTestCase
/** @test */ /** @test */
public function user_can_create_a_product_with_only_cash_price() public function user_can_create_a_product_with_only_cash_price()
{ {
$unit = factory(Unit::class)->create(['name' => 'Testing 123']);
$this->loginAsUser(); $this->loginAsUser();
$this->visit(route('products.index')); $this->visit(route('products.index'));
@ -97,6 +103,7 @@ class ManageProductsTest extends BrowserKitTestCase
$this->type('Product 1', 'name'); $this->type('Product 1', 'name');
$this->type('1000', 'cash_price'); $this->type('1000', 'cash_price');
$this->type('', 'credit_price'); $this->type('', 'credit_price');
$this->type($unit->id, 'unit_id');
$this->press(trans('product.create')); $this->press(trans('product.create'));
$this->seePageIs(route('products.index')); $this->seePageIs(route('products.index'));
@ -112,6 +119,7 @@ class ManageProductsTest extends BrowserKitTestCase
/** @test */ /** @test */
public function user_can_edit_a_product() public function user_can_edit_a_product()
{ {
$unit = factory(Unit::class)->create(['name' => 'Testing 123']);
$this->loginAsUser(); $this->loginAsUser();
$product = factory(Product::class)->create(); $product = factory(Product::class)->create();
@ -122,6 +130,7 @@ class ManageProductsTest extends BrowserKitTestCase
$this->type('Product 1', 'name'); $this->type('Product 1', 'name');
$this->type('1000', 'cash_price'); $this->type('1000', 'cash_price');
$this->type('1200', 'credit_price'); $this->type('1200', 'credit_price');
$this->type($unit->id, 'unit_id');
$this->press(trans('product.update')); $this->press(trans('product.update'));
$this->seeInDatabase('products', [ $this->seeInDatabase('products', [

108
tests/Feature/ManageUnitsTest.php

@ -0,0 +1,108 @@
<?php
namespace Tests\Feature;
use App\Product;
use App\Unit;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Tests\BrowserKitTestCase;
class ManageUnitsTest extends BrowserKitTestCase
{
use DatabaseMigrations;
/** @test */
public function user_can_see_unit_list()
{
$unit1 = factory(Unit::class)->create(['name' => 'Testing 123']);
$unit2 = factory(Unit::class)->create(['name' => 'Testing 456']);
$this->loginAsUser();
$this->visit(route('units.index'));
$this->see($unit1->name);
$this->see($unit2->name);
}
/** @test */
public function user_can_create_a_unit()
{
$this->loginAsUser();
$this->visit(route('units.index'));
$this->click(trans('unit.create'));
$this->seePageIs(route('units.index', ['action' => 'create']));
$this->type('Unit 1', 'name');
$this->press(trans('unit.create'));
$this->seePageIs(route('units.index'));
$this->see(trans('unit.created'));
$this->seeInDatabase('product_units', [
'name' => 'Unit 1',
]);
}
/** @test */
public function user_can_edit_a_unit()
{
$this->loginAsUser();
$unit = factory(Unit::class)->create();
$this->visit(route('units.index'));
$this->click('edit-unit-' . $unit->id);
$this->seePageIs(route('units.index', ['action' => 'edit','id' => $unit->id]));
$this->type('Unit 1', 'name');
$this->press(trans('unit.update'));
$this->see(trans('unit.updated'));
$this->seePageIs(route('units.index'));
$this->seeInDatabase('product_units', [
'name' => 'Unit 1',
]);
}
/** @test */
public function user_can_delete_a_unit()
{
$this->loginAsUser();
$unit = factory(Unit::class)->create();
$this->visit(route('units.index'));
$this->click('del-unit-' . $unit->id);
$this->seePageIs(route('units.index', ['action' => 'delete','id' => $unit->id]));
$this->seeInDatabase('product_units', [
'id' => $unit->id
]);
$this->press(trans('app.delete_confirm_button'));
$this->dontSeeInDatabase('product_units', [
'id' => $unit->id
]);
}
/** @test */
public function user_can_not_delete_a_unit_that_has_product()
{
$this->loginAsUser();
$product = factory(Product::class)->create();
$unitId = $product->unit_id;
$this->visit(route('units.index'));
$this->click('del-unit-' . $unitId);
$this->seePageIs(route('units.index', ['action' => 'delete','id' => $unitId]));
$this->press(trans('app.delete_confirm_button'));
$this->see(trans('unit.undeleted'));
$this->seePageIs(route('units.index', ['action' => 'delete','id' => $unitId]));
$this->seeInDatabase('product_units', [
'id' => $unitId
]);
}
}
Loading…
Cancel
Save