diff --git a/DataTables/ProductDataTable.php b/DataTables/ProductDataTable.php new file mode 100644 index 0000000..495ba60 --- /dev/null +++ b/DataTables/ProductDataTable.php @@ -0,0 +1,113 @@ +with('category', 'unit'); + + return datatables() + ->eloquent($query) + ->filter(function ($query) { + $search = request()->get('search'); + if ($search['value'] !== "") { + $query->where('name', 'like', "%" . $search['value'] . "%"); + $query->orWhere('harga_beli_ppn', 'like', "%" . $search['value'] . "%"); + $query->orWhere('harga_beli_non_ppn', 'like', "%" . $search['value'] . "%"); + $query->orWhereRelation('category', 'name', 'like', "%" . $search['value'] . "%"); + $query->orWhereRelation('unit', 'name', 'like', "%" . $search['value'] . "%"); + + } + }) + ->addIndexColumn() + ->addColumn('status', function ($model) { + return view('product::product._status', compact('model')); + }) + ->addColumn('action', function ($model) { + return view('product::product._action', compact('model')); + }) + ->rawColumns(['status', 'action']); + } + + /** + * Get query source of dataTable. + * + * @param \Modules\Product\Entities\Product $model + * + * @return \Illuminate\Database\Eloquent\Builder + */ + public function query(Product $model) + { + return $model->newQuery(); + } + + /** + * Optional method if you want to use html builder. + * + * @return \Yajra\DataTables\Html\Builder + */ + public function html() + { + return $this->builder() + ->setTableId('product-table') + ->columns($this->getColumns()) + ->minifiedAjax() + ->orderBy(1, 'asc') + ->stateSave(false) + ->responsive() + ->autoWidth(false) + ->parameters([ + 'scrollX' => false, + 'drawCallback' => 'function() { KTMenu.createInstances(); }', + ]) + ->addTableClass('align-middle table-row-dashed fs-6 gy-5'); + } + + /** + * Get columns. + * + * @return array + */ + protected function getColumns() + { + return [ + Column::make('DT_RowIndex')->title('No')->orderable(false)->searchable(false), + Column::make('name')->title(__('Name')), + Column::make('category.name')->title(__('Category')), + Column::make('unit.name')->title(__('Unit')), + Column::make('harga_beli_ppn')->title(__('Harga Beli PPN')), + Column::make('harga_beli_non_ppn')->title(__('Harga Beli Non PPN')), + Column::computed('status')->title(__('Status'))->width(50)->addClass('text-center')->exportable(false), + Column::computed('action') + ->exportable(false) + ->printable(false) + ->width(100) + ->addClass('text-center') + ->responsivePriority(-1), + ]; + } + + /** + * Get filename for export. + * + * @return string + */ + protected function filename() + : string + { + return 'Product_' . date('YmdHis'); + } + } diff --git a/Database/Migrations/2023_06_10_122156_create_products_table.php b/Database/Migrations/2023_06_10_122156_create_products_table.php new file mode 100644 index 0000000..e9b8377 --- /dev/null +++ b/Database/Migrations/2023_06_10_122156_create_products_table.php @@ -0,0 +1,44 @@ +id(); + $table->foreignIdFor(Category::class)->constrained("categories")->onDelete("cascade"); + $table->foreignIdFor(Unit::class)->constrained("units")->onDelete("cascade"); + $table->string("name"); + $table->string("harga_beli_ppn")->nullable(); + $table->string("harga_beli_non_ppn")->nullable(); + $table->string("status")->default(1); + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId("created_by")->nullable()->constrained("users"); + $table->foreignId("updated_by")->nullable()->constrained("users"); + $table->foreignId("deleted_by")->nullable()->constrained("users"); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('products'); + } +}; diff --git a/Entities/Category.php b/Entities/Category.php index a0e79df..627ee12 100644 --- a/Entities/Category.php +++ b/Entities/Category.php @@ -11,4 +11,9 @@ 'updated_by', 'deleted_by', ]; + + public function products() + { + return $this->hasMany(Product::class); + } } diff --git a/Entities/Product.php b/Entities/Product.php new file mode 100644 index 0000000..bed254a --- /dev/null +++ b/Entities/Product.php @@ -0,0 +1,25 @@ +belongsTo(Category::class); + } + + public function unit() + { + return $this->belongsTo(Unit::class); + } + } diff --git a/Entities/Unit.php b/Entities/Unit.php index e24726b..0363cc6 100644 --- a/Entities/Unit.php +++ b/Entities/Unit.php @@ -11,4 +11,9 @@ 'updated_by', 'deleted_by', ]; + + public function products() + { + return $this->hasMany(Product::class); + } } diff --git a/Http/Controllers/ProductController.php b/Http/Controllers/ProductController.php new file mode 100644 index 0000000..38d934c --- /dev/null +++ b/Http/Controllers/ProductController.php @@ -0,0 +1,178 @@ +middleware(function ($request, $next) { + $this->user = Auth::guard('web')->user(); + return $next($request); + }); + + $module = file_get_contents(dirname(__FILE__, 3) . '/module.json'); + $this->module = json_decode($module); + } + + /** + * Display a listing of the resource. + * + * @return Renderable + */ + public function index(ProductDataTable $dataTable) + { + if (is_null($this->user) || !$this->user->can($this->module->alias . '.read')) { + abort(403, 'Sorry !! You are Unauthorized to view any ' . $this->module->alias . ' !'); + } + $categories = Category::where('status',1)->get(); + $units = Unit::where('status',1)->get(); + + return $dataTable->render($this->module->alias . '::product.index', compact('categories', 'units')); + } + + /** + * Store a newly created resource in storage. + * + * @param Request $request + * + * @return Renderable + */ + public function store(StoreProductRequest $request) + { + if (is_null($this->user) || !$this->user->can($this->module->alias . '.create')) { + abort(403, 'Sorry !! You are Unauthorized to create any ' . $this->module->alias . ' !'); + } + + //Validate the request + $validated = $request->validated(); + + // Store the Product... + if ($validated) { + try { + $this->model::create($validated); + echo json_encode(['status' => 'success', 'message' => $this->module->name . ' product created successfully.']); + } catch (Exception $e) { + echo json_encode(['status' => 'error', 'message' => $this->module->name . ' product created failed.']); + } + return; + } + + echo json_encode(['status' => 'error', 'message' => $this->module->name . ' product created failed.']); + } + + /** + * Show the form for creating a new resource. + * + * @return Renderable + */ + public function create() + { + if (is_null($this->user) || !$this->user->can($this->module->alias . '.create')) { + abort(403, 'Sorry !! You are Unauthorized to create any ' . $this->module->alias . ' !'); + } + + abort(404); + } + + /** + * Show the specified resource. + * + * @param int $id + * + * @return Renderable + */ + public function show($id) + { + if (is_null($this->user) || !$this->user->can($this->module->alias . '.read')) { + abort(403, 'Sorry !! You are Unauthorized to view any ' . $this->module->alias . ' !'); + } + + abort(404); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * + * @return Renderable + */ + public function edit($id) + { + if (is_null($this->user) || !$this->user->can($this->module->alias . '.update')) { + abort(403, 'Sorry !! You are Unauthorized to update any ' . $this->module->alias . ' !'); + } + + $data = $this->model::find($id); + echo json_encode($data); + } + + /** + * Update the specified resource in storage. + * + * @param Request $request + * @param int $id + * + * @return Renderable + */ + public function update(UpdateProductRequest $request, Product $product) + { + if (is_null($this->user) || !$this->user->can($this->module->alias . '.update')) { + abort(403, 'Sorry !! You are Unauthorized to update any ' . $this->module->alias . ' !'); + } + + //Validate the request + $validated = $request->validated(); + + // Update the Product... + if ($validated) { + try { + $product->update($validated); + echo json_encode(['status' => 'success', 'message' => $this->module->name . ' product updated successfully.']); + } catch (Exception $e) { + echo json_encode(['status' => 'error', 'message' => $this->module->name . ' product updated failed.']); + } + return; + } + + echo json_encode(['status' => 'error', 'message' => $this->module->name . ' product updated failed.']); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * + * @return Renderable + */ + public function destroy(Product $product) + { + if (is_null($this->user) || !$this->user->can($this->module->alias . '.delete')) { + abort(403, 'Sorry !! You are Unauthorized to delete any ' . $this->module->alias . ' !'); + } + + try { + $product->delete(); + echo json_encode(['status' => 'success', 'message' => $this->module->name . ' product deleted successfully.']); + } catch (Exception $e) { + echo json_encode(['status' => 'error', 'message' => $this->module->name . ' product deleted failed.']); + } + } + } diff --git a/Http/Requests/Category/StoreCategoryRequest.php b/Http/Requests/Category/StoreCategoryRequest.php index aa2fa8c..751ea5f 100644 --- a/Http/Requests/Category/StoreCategoryRequest.php +++ b/Http/Requests/Category/StoreCategoryRequest.php @@ -46,7 +46,7 @@ foreach ($errors as $key => $value) { flash($value[0]); } - return redirect()->route('master.category.index')->with('error', 'Category created failed.'); + return redirect()->route('product.category.index')->with('error', 'Category created failed.'); } }); diff --git a/Http/Requests/Category/UpdateCategoryRequest.php b/Http/Requests/Category/UpdateCategoryRequest.php index 76807bc..4a1fdd9 100644 --- a/Http/Requests/Category/UpdateCategoryRequest.php +++ b/Http/Requests/Category/UpdateCategoryRequest.php @@ -46,7 +46,7 @@ foreach ($errors as $key => $value) { flash($value[0]); } - return redirect()->route('master.category.index')->with('error', 'Category updated failed.'); + return redirect()->route('product.category.index')->with('error', 'Category updated failed.'); } }); diff --git a/Http/Requests/Product/StoreProductRequest.php b/Http/Requests/Product/StoreProductRequest.php new file mode 100644 index 0000000..ad3525a --- /dev/null +++ b/Http/Requests/Product/StoreProductRequest.php @@ -0,0 +1,71 @@ + + */ + public function rules() + : array + { + return [ + 'name' => 'required|string|max:50|unique:products,name', + 'unit_id' => 'required|integer|exists:units,id', + 'category_id' => 'required|integer|exists:categories,id', + 'harga_beli_ppn' => 'required|integer', + 'harga_beli_non_ppn' => 'required|integer', + 'status' => 'nullable|integer|max:1', + ]; + } + + /** + * Configure the validator instance. + */ + public function withValidator(Validator $validator) + : void + { + $validator->after(function (Validator $validator) { + if ($validator->errors()->any()) { + $errors = json_decode($validator->errors()->toJson(), true); + + foreach ($errors as $key => $value) { + flash($value[0]); + } + return redirect()->route('product.index')->with('error', 'Product created failed.'); + } + + }); + } + + protected function failedValidation(Validator|\Illuminate\Contracts\Validation\Validator $validator) + : JsonResponse + { + $errors = (new ValidationException($validator))->errors(); + + throw new HttpResponseException(response()->json([ + 'success' => false, + 'errors' => $errors, + 'messages' => 'Product created failed.' + ], JsonResponse::HTTP_UNPROCESSABLE_ENTITY)); + } + + } diff --git a/Http/Requests/Product/UpdateProductRequest.php b/Http/Requests/Product/UpdateProductRequest.php new file mode 100644 index 0000000..6fcf977 --- /dev/null +++ b/Http/Requests/Product/UpdateProductRequest.php @@ -0,0 +1,71 @@ + + */ + public function rules() + : array + { + return [ + 'name' => 'required|string|max:50|unique:products,name,' . $this->product->id, + 'unit_id' => 'required|integer|exists:units,id', + 'category_id' => 'required|integer|exists:categories,id', + 'harga_beli_ppn' => 'required|integer', + 'harga_beli_non_ppn' => 'required|integer', + 'status' => 'nullable|integer|max:1', + ]; + } + + /** + * Configure the validator instance. + */ + public function withValidator(Validator $validator) + : void + { + $validator->after(function (Validator $validator) { + if ($validator->errors()->any()) { + $errors = json_decode($validator->errors()->toJson(), true); + + foreach ($errors as $key => $value) { + flash($value[0]); + } + return redirect()->route('product.index')->with('error', 'Product updated failed.'); + } + + }); + } + + protected function failedValidation(Validator|\Illuminate\Contracts\Validation\Validator $validator) + : JsonResponse + { + $errors = (new ValidationException($validator))->errors(); + + throw new HttpResponseException(response()->json([ + 'success' => false, + 'errors' => $errors, + 'messages' => 'Product updated failed.' + ], JsonResponse::HTTP_UNPROCESSABLE_ENTITY)); + } + + } diff --git a/Http/Requests/Unit/StoreUnitRequest.php b/Http/Requests/Unit/StoreUnitRequest.php index 4cca898..6e3da06 100644 --- a/Http/Requests/Unit/StoreUnitRequest.php +++ b/Http/Requests/Unit/StoreUnitRequest.php @@ -46,7 +46,7 @@ foreach ($errors as $key => $value) { flash($value[0]); } - return redirect()->route('master.unit.index')->with('error', 'Unit created failed.'); + return redirect()->route('product.unit.index')->with('error', 'Unit created failed.'); } }); diff --git a/Http/Requests/Unit/UpdateUnitRequest.php b/Http/Requests/Unit/UpdateUnitRequest.php index 1b648f0..b9c2c15 100644 --- a/Http/Requests/Unit/UpdateUnitRequest.php +++ b/Http/Requests/Unit/UpdateUnitRequest.php @@ -46,7 +46,7 @@ foreach ($errors as $key => $value) { flash($value[0]); } - return redirect()->route('master.unit.index')->with('error', 'Unit updated failed.'); + return redirect()->route('product.unit.index')->with('error', 'Unit updated failed.'); } }); diff --git a/Resources/views/partials/menu/_app.blade.php b/Resources/views/partials/menu/_app.blade.php index baa96b8..de3f70c 100644 --- a/Resources/views/partials/menu/_app.blade.php +++ b/Resources/views/partials/menu/_app.blade.php @@ -35,6 +35,19 @@ + + + + diff --git a/Resources/views/product/_action.blade.php b/Resources/views/product/_action.blade.php new file mode 100644 index 0000000..61b3c22 --- /dev/null +++ b/Resources/views/product/_action.blade.php @@ -0,0 +1,13 @@ +@php + $route = explode('.', Route::currentRouteName()); +@endphp +
+ + {!! getIcon("pencil", "fs-1 text-info","duotune") !!} + + + {!! Form::open(['method' => 'DELETE','route' => [$route[0].'.destroy', $model->id],'class'=>'']) !!} + {{ Form::button(getIcon("trash", "fs-1 text-danger","duotune"), ['type' => 'submit', 'class' => 'delete btn btn-icon btn-bg-light btn-active-light-danger btn-sm'] ) }} + {!! Form::close() !!} +
diff --git a/Resources/views/product/_form.blade.php b/Resources/views/product/_form.blade.php new file mode 100644 index 0000000..07977ee --- /dev/null +++ b/Resources/views/product/_form.blade.php @@ -0,0 +1,117 @@ +@php + $route = explode('.', Route::currentRouteName()); +@endphp + + + + diff --git a/Resources/views/product/_status.blade.php b/Resources/views/product/_status.blade.php new file mode 100644 index 0000000..370e5fd --- /dev/null +++ b/Resources/views/product/_status.blade.php @@ -0,0 +1,14 @@ +@php + $route = explode('.', Route::currentRouteName()); +@endphp + +{!! Form::open(['method' => 'PUT','route' => [$route[0].'.update', $model->id],'class'=>'']) !!} +
+ status==1 ? 'checked' : '' }} type="checkbox" name="status" id="status"/> + + + + + +
+{!! Form::close() !!} diff --git a/Resources/views/product/_table.blade.php b/Resources/views/product/_table.blade.php new file mode 100644 index 0000000..3253927 --- /dev/null +++ b/Resources/views/product/_table.blade.php @@ -0,0 +1,136 @@ + +{{ $dataTable->table() }} + + +{{-- Inject Scripts --}} +@section('scripts') + {{ $dataTable->scripts() }} +@endsection + +@push('customscript') + @php + $route = explode('.', Route::currentRouteName()); + @endphp + + +@endpush + +@section('styles') + +@endsection diff --git a/Resources/views/product/index.blade.php b/Resources/views/product/index.blade.php new file mode 100644 index 0000000..a53aaca --- /dev/null +++ b/Resources/views/product/index.blade.php @@ -0,0 +1,137 @@ +@php + $route = explode('.', Route::currentRouteName()); +@endphp + + + +
+ +
+
+
+ + + + + + + + + +
+ + +
+ + +
+ +
+ + + + + + + +
+ + +
+
+
+ @include($route[0].'::product._table') + @include($route[0].'::product._form') +
+ +
+ + @push('customscript') + + @endpush +
diff --git a/Routes/web.php b/Routes/web.php index b4f0861..cb827e5 100644 --- a/Routes/web.php +++ b/Routes/web.php @@ -12,9 +12,11 @@ */ use Modules\Product\Http\Controllers\CategoryController; + use Modules\Product\Http\Controllers\ProductController; use Modules\Product\Http\Controllers\UnitController; Route::middleware(['auth', 'verified'])->group(function () { + Route::resource('product', ProductController::class); Route::prefix('product')->name('product.')->group(function () { Route::resource('unit', UnitController::class); Route::resource('category', CategoryController::class);