create crud product categories and partial update crud products with dealers stock

This commit is contained in:
2025-05-28 18:24:44 +07:00
parent 80375d8af3
commit 59e23ae535
28 changed files with 1336 additions and 28 deletions

View File

@@ -0,0 +1,124 @@
<?php
namespace App\Http\Controllers\WarehouseManagement;
use App\Http\Controllers\Controller;
use App\Models\Menu;
use App\Models\ProductCategory;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Yajra\DataTables\Facades\DataTables;
class ProductCategoriesController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$menu = Menu::where('link','product_categories.index')->first();
if($request->ajax()){
$data = ProductCategory::query();
return DataTables::of($data)
->addIndexColumn()
->addColumn('action', function ($row) use ($menu) {
$btn = '';
if (Auth::user()->can('delete', $menu)) {
$btn .= '<button class="btn btn-danger btn-sm btn-destroy-product-category" data-action="' . route('product_categories.destroy', $row->id) . '" data-id="' . $row->id . '">Hapus</button>';
}
if (Auth::user()->can('update', $menu)) {
$btn .= '<button class="btn btn-warning btn-sm btn-edit-product-category" data-url="' . route('product_categories.edit', $row->id) . '" data-action="' . route('product_categories.update', $row->id) . '" data-id="' . $row->id . '">Edit</button>';
}
return $btn;
})
->rawColumns(['action'])
->make(true);
}
return view('warehouse_management.product_categories.index');
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
]);
ProductCategory::create($validated);
return response()->json(['success' => true, 'message' => 'Kategori berhasil ditambahkan.']);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$category = ProductCategory::findOrFail($id);
return response()->json($category);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
]);
$category = ProductCategory::findOrFail($id);
$category->update($validated);
return response()->json(['success' => true, 'message' => 'Kategori berhasil diperbarui.']);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
ProductCategory::findOrFail($id)->delete();
return response()->json(['success' => true, 'message' => 'Kategorii berhasil dihapus.']);
}
}

View File

@@ -0,0 +1,120 @@
<?php
namespace App\Http\Controllers\WarehouseManagement;
use App\Http\Controllers\Controller;
use App\Models\Menu;
use App\Models\Product;
use App\Models\ProductCategory;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Yajra\DataTables\Facades\DataTables;
class ProductsController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$menu = Menu::where('link','products.index')->first();
if($request->ajax()){
$data = Product::with(['category','dealers']);
return DataTables::of($data)
->addIndexColumn()
->addColumn('category_name', function ($row) {
return $row->category ? $row->category->name : '-';
})
->addColumn('total_stock', function ($row){
return $row->dealers->sum(function($dealer){
return $dealer->pivot->quantity ?? 0;
});
})
->addColumn('action', function ($row) use ($menu) {
$btn = '';
if (Auth::user()->can('delete', $menu)) {
$btn .= '<button class="btn btn-danger btn-sm btn-destroy-product" data-action="' . route('products.destroy', $row->id) . '" data-id="' . $row->id . '">Hapus</button>';
}
if (Auth::user()->can('update', $menu)) {
$btn .= '<button class="btn btn-warning btn-sm btn-edit-product" data-url="' . route('products.edit', $row->id) . '" data-action="' . route('products.update', $row->id) . '" data-id="' . $row->id . '">Edit</button>';
}
return $btn;
})
->rawColumns(['action'])
->make(true);
}
return view('warehouse_management.products.index');
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}

View File

@@ -22,4 +22,10 @@ class Dealer extends Model
{ {
return $this->hasMany(Transaction::class, 'dealer_id', 'id'); return $this->hasMany(Transaction::class, 'dealer_id', 'id');
} }
public function products(){
return $this->belongsToMany(Product::class, 'stock')
->withPivot('quantity')
->withTimestamps();
}
} }

24
app/Models/Product.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Product extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = ['code','name','description','product_category_id'];
public function category(){
return $this->belongsTo(ProductCategory::class, 'product_category_id');
}
public function dealers(){
return $this->belongsToMany(Dealer::class, 'stock')
->withPivot('quantity')
->withTimestamps();
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class ProductCategory extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = ['name'];
public function products(){
return $this->hasMany(Product::class, 'product_category_id');
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Providers; namespace App\Providers;
use App\Models\Menu; use App\Models\Menu;
use Carbon\Carbon;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\View;
@@ -25,7 +26,8 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function boot() public function boot()
{ {
View::composer(['layouts.partials.sidebarMenu', 'dashboard', 'dealer_recap', 'back.*'], function ($view) { Carbon::setLocale('id');
View::composer(['layouts.partials.sidebarMenu', 'dashboard', 'dealer_recap', 'back.*', 'warehouse_management.*'], function ($view) {
$menuQuery = Menu::all(); $menuQuery = Menu::all();
$menus = []; $menus = [];
foreach($menuQuery as $menu) { foreach($menuQuery as $menu) {

View File

@@ -14,6 +14,7 @@
"laravel/tinker": "^2.5", "laravel/tinker": "^2.5",
"laravel/ui": "^3.4", "laravel/ui": "^3.4",
"maatwebsite/excel": "^3.1", "maatwebsite/excel": "^3.1",
"nesbot/carbon": "^2.73",
"yajra/laravel-datatables-oracle": "^9.20" "yajra/laravel-datatables-oracle": "^9.20"
}, },
"require-dev": { "require-dev": {

171
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "f96a1847e52c9eb29681f0fb8fea48c2", "content-hash": "3e3b47389d4bce664f705134cae65b49",
"packages": [ "packages": [
{ {
"name": "asm89/stack-cors", "name": "asm89/stack-cors",
@@ -122,6 +122,75 @@
], ],
"time": "2021-08-15T20:50:18+00:00" "time": "2021-08-15T20:50:18+00:00"
}, },
{
"name": "carbonphp/carbon-doctrine-types",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/CarbonPHP/carbon-doctrine-types.git",
"reference": "3c430083d0b41ceed84ecccf9dac613241d7305d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/3c430083d0b41ceed84ecccf9dac613241d7305d",
"reference": "3c430083d0b41ceed84ecccf9dac613241d7305d",
"shasum": ""
},
"require": {
"php": "^7.1.8 || ^8.0"
},
"conflict": {
"doctrine/dbal": ">=3.7.0"
},
"require-dev": {
"doctrine/dbal": ">=2.0.0",
"nesbot/carbon": "^2.71.0 || ^3.0.0",
"phpunit/phpunit": "^10.3"
},
"type": "library",
"autoload": {
"psr-4": {
"Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "KyleKatarn",
"email": "kylekatarnls@gmail.com"
}
],
"description": "Types to use Carbon in Doctrine",
"keywords": [
"carbon",
"date",
"datetime",
"doctrine",
"time"
],
"support": {
"issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues",
"source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/1.0.0"
},
"funding": [
{
"url": "https://github.com/kylekatarnls",
"type": "github"
},
{
"url": "https://opencollective.com/Carbon",
"type": "open_collective"
},
{
"url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
"type": "tidelift"
}
],
"time": "2023-10-01T12:35:29+00:00"
},
{ {
"name": "dflydev/dot-access-data", "name": "dflydev/dot-access-data",
"version": "v3.0.1", "version": "v3.0.1",
@@ -2537,35 +2606,41 @@
}, },
{ {
"name": "nesbot/carbon", "name": "nesbot/carbon",
"version": "2.58.0", "version": "2.73.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/briannesbitt/Carbon.git", "url": "https://github.com/CarbonPHP/carbon.git",
"reference": "97a34af22bde8d0ac20ab34b29d7bfe360902055" "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/97a34af22bde8d0ac20ab34b29d7bfe360902055", "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/9228ce90e1035ff2f0db84b40ec2e023ed802075",
"reference": "97a34af22bde8d0ac20ab34b29d7bfe360902055", "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"carbonphp/carbon-doctrine-types": "*",
"ext-json": "*", "ext-json": "*",
"php": "^7.1.8 || ^8.0", "php": "^7.1.8 || ^8.0",
"psr/clock": "^1.0",
"symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-mbstring": "^1.0",
"symfony/polyfill-php80": "^1.16", "symfony/polyfill-php80": "^1.16",
"symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0"
}, },
"provide": {
"psr/clock-implementation": "1.0"
},
"require-dev": { "require-dev": {
"doctrine/dbal": "^2.0 || ^3.0", "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0",
"doctrine/orm": "^2.7", "doctrine/orm": "^2.7 || ^3.0",
"friendsofphp/php-cs-fixer": "^3.0", "friendsofphp/php-cs-fixer": "^3.0",
"kylekatarnls/multi-tester": "^2.0", "kylekatarnls/multi-tester": "^2.0",
"ondrejmirtes/better-reflection": "<6",
"phpmd/phpmd": "^2.9", "phpmd/phpmd": "^2.9",
"phpstan/extension-installer": "^1.0", "phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12.54 || ^1.0", "phpstan/phpstan": "^0.12.99 || ^1.7.14",
"phpunit/php-file-iterator": "^2.0.5", "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6",
"phpunit/phpunit": "^7.5.20 || ^8.5.23", "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20",
"squizlabs/php_codesniffer": "^3.4" "squizlabs/php_codesniffer": "^3.4"
}, },
"bin": [ "bin": [
@@ -2573,10 +2648,6 @@
], ],
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": {
"dev-3.x": "3.x-dev",
"dev-master": "2.x-dev"
},
"laravel": { "laravel": {
"providers": [ "providers": [
"Carbon\\Laravel\\ServiceProvider" "Carbon\\Laravel\\ServiceProvider"
@@ -2586,6 +2657,10 @@
"includes": [ "includes": [
"extension.neon" "extension.neon"
] ]
},
"branch-alias": {
"dev-2.x": "2.x-dev",
"dev-master": "3.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -2622,15 +2697,19 @@
}, },
"funding": [ "funding": [
{ {
"url": "https://opencollective.com/Carbon", "url": "https://github.com/sponsors/kylekatarnls",
"type": "open_collective" "type": "github"
}, },
{ {
"url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", "url": "https://opencollective.com/Carbon#sponsor",
"type": "opencollective"
},
{
"url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-04-25T19:31:17+00:00" "time": "2025-01-08T20:10:23+00:00"
}, },
{ {
"name": "nette/schema", "name": "nette/schema",
@@ -3124,6 +3203,54 @@
}, },
"time": "2016-08-06T20:24:11+00:00" "time": "2016-08-06T20:24:11+00:00"
}, },
{
"name": "psr/clock",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/clock.git",
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Psr\\Clock\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for reading the clock.",
"homepage": "https://github.com/php-fig/clock",
"keywords": [
"clock",
"now",
"psr",
"psr-20",
"time"
],
"support": {
"issues": "https://github.com/php-fig/clock/issues",
"source": "https://github.com/php-fig/clock/tree/1.0.0"
},
"time": "2022-11-25T14:36:26+00:00"
},
{ {
"name": "psr/container", "name": "psr/container",
"version": "1.1.2", "version": "1.1.2",
@@ -8917,12 +9044,12 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "dev", "minimum-stability": "dev",
"stability-flags": [], "stability-flags": {},
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": "^7.3|^8.0" "php": "^7.3|^8.0"
}, },
"platform-dev": [], "platform-dev": {},
"plugin-api-version": "2.1.0" "plugin-api-version": "2.6.0"
} }

View File

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

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('code')->unique();
$table->string('name');
$table->text('description')->nullable();
$table->foreignId('product_category_id')->constrained()->onDelete('cascade');
$table->softDeletes();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('products');
}
}

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateStockTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('stock', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id')->constrained()->onDelete('cascade');
$table->foreignId('dealer_id')->constrained()->onDelete('cascade');
$table->integer('quantity')->default(0);
$table->timestamps();
$table->unique(['product_id','dealer_id']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('stock');
}
}

View File

@@ -14,5 +14,10 @@ class DatabaseSeeder extends Seeder
public function run() public function run()
{ {
// \App\Models\User::factory(10)->create(); // \App\Models\User::factory(10)->create();
$this->call([
MenuSeeder::class,
ProductAndCategorySeeder::class,
UserRoleAndPrivilegesSeeder::class
]);
} }
} }

View File

@@ -0,0 +1,39 @@
<?php
namespace Database\Seeders;
use App\Models\Menu;
use Illuminate\Database\Seeder;
class MenuSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$menus = [
[
'name' => 'Produk',
'link' => 'products.index'
],
[
'name' => 'Kategori Produk',
'link' => 'product_categories.index'
]
];
foreach ($menus as $menu){
Menu::updateOrInsert(
['link' => $menu['link']],
[
'name' => $menu['name'],
'created_at' => now(),
'updated_at' => now()
]
);
}
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Database\Seeders;
use App\Models\Product;
use App\Models\ProductCategory;
use Illuminate\Database\Seeder;
class ProductAndCategorySeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$categories = [
'Oli & Pelumas' => [
['code' => 'OLI001', 'name' => 'Oli Mesin 10W-40'],
['code' => 'OLI002', 'name' => 'Oli Gardan'],
],
'Aki & Kelistrikan' => [
['code' => 'AKI001', 'name' => 'Aki Kering 12V'],
['code' => 'AKI002', 'name' => 'Regulator Rectifier'],
],
'Rem' => [
['code' => 'REM001', 'name' => 'Kampas Rem Belakang'],
['code' => 'REM002', 'name' => 'Cakram Depan'],
],
];
foreach ($categories as $categoryName => $products) {
$category = ProductCategory::firstOrCreate(
['name' => $categoryName],
['created_at' => now(), 'updated_at' => now()]
);
foreach ($products as $product) {
Product::updateOrCreate(
['code' => $product['code']],
[
'name' => $product['name'],
'description' => $product['name'],
'product_category_id' => $category->id
]
);
}
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Database\Seeders;
use App\Models\Menu;
use App\Models\Privilege;
use App\Models\Role;
use Illuminate\Database\Seeder;
class UserRoleAndPrivilegesSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$role = Role::firstOrCreate(['name' => 'admin']);
$menus = Menu::all();
foreach ($menus as $menu) {
Privilege::updateOrCreate(
[
'role_id' => $role->id,
'menu_id' => $menu->id
],
[
'create' => 1,
'update' => 1,
'delete' => 1,
'view' => 1
]
);
}
}
}

32
public/js/index.js Normal file
View File

@@ -0,0 +1,32 @@
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./resources/js/warehouse_management/product_categories/index.js":
/*!***********************************************************************!*\
!*** ./resources/js/warehouse_management/product_categories/index.js ***!
\***********************************************************************/
/***/ (() => {
eval("$(function () {\n $('#product-categories-table').DataTable({\n processing: true,\n serverSide: true,\n ajax: '{{ route(\"product_categories.index\") }}',\n columns: [{\n data: 'DT_RowIndex',\n name: 'DT_RowIndex',\n orderable: false,\n searchable: false\n }, {\n data: 'name',\n name: 'name'\n }, {\n data: 'created_at',\n name: 'created_at'\n }, {\n data: 'action',\n name: 'action',\n orderable: false,\n searchable: false\n }]\n });\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvdXJjZXMvanMvd2FyZWhvdXNlX21hbmFnZW1lbnQvcHJvZHVjdF9jYXRlZ29yaWVzL2luZGV4LmpzLmpzIiwibmFtZXMiOlsiJCIsIkRhdGFUYWJsZSIsInByb2Nlc3NpbmciLCJzZXJ2ZXJTaWRlIiwiYWpheCIsImNvbHVtbnMiLCJkYXRhIiwibmFtZSIsIm9yZGVyYWJsZSIsInNlYXJjaGFibGUiXSwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL3Jlc291cmNlcy9qcy93YXJlaG91c2VfbWFuYWdlbWVudC9wcm9kdWN0X2NhdGVnb3JpZXMvaW5kZXguanM/ZmM4NCJdLCJzb3VyY2VzQ29udGVudCI6WyIkKGZ1bmN0aW9uICgpIHtcbiAgJCgnI3Byb2R1Y3QtY2F0ZWdvcmllcy10YWJsZScpLkRhdGFUYWJsZSh7XG4gICAgICBwcm9jZXNzaW5nOiB0cnVlLFxuICAgICAgc2VydmVyU2lkZTogdHJ1ZSxcbiAgICAgIGFqYXg6ICd7eyByb3V0ZShcInByb2R1Y3RfY2F0ZWdvcmllcy5pbmRleFwiKSB9fScsXG4gICAgICBjb2x1bW5zOiBbXG4gICAgICAgICAgeyBkYXRhOiAnRFRfUm93SW5kZXgnLCBuYW1lOiAnRFRfUm93SW5kZXgnLCBvcmRlcmFibGU6IGZhbHNlLCBzZWFyY2hhYmxlOiBmYWxzZSB9LFxuICAgICAgICAgIHsgZGF0YTogJ25hbWUnLCBuYW1lOiAnbmFtZScgfSxcbiAgICAgICAgICB7IGRhdGE6ICdjcmVhdGVkX2F0JywgbmFtZTogJ2NyZWF0ZWRfYXQnIH0sXG4gICAgICAgICAgeyBkYXRhOiAnYWN0aW9uJywgbmFtZTogJ2FjdGlvbicsIG9yZGVyYWJsZTogZmFsc2UsIHNlYXJjaGFibGU6IGZhbHNlIH1cbiAgICAgIF1cbiAgfSk7XG59KTsiXSwibWFwcGluZ3MiOiJBQUFBQSxDQUFDLENBQUMsWUFBWTtFQUNaQSxDQUFDLENBQUMsMkJBQUQsQ0FBRCxDQUErQkMsU0FBL0IsQ0FBeUM7SUFDckNDLFVBQVUsRUFBRSxJQUR5QjtJQUVyQ0MsVUFBVSxFQUFFLElBRnlCO0lBR3JDQyxJQUFJLEVBQUUseUNBSCtCO0lBSXJDQyxPQUFPLEVBQUUsQ0FDTDtNQUFFQyxJQUFJLEVBQUUsYUFBUjtNQUF1QkMsSUFBSSxFQUFFLGFBQTdCO01BQTRDQyxTQUFTLEVBQUUsS0FBdkQ7TUFBOERDLFVBQVUsRUFBRTtJQUExRSxDQURLLEVBRUw7TUFBRUgsSUFBSSxFQUFFLE1BQVI7TUFBZ0JDLElBQUksRUFBRTtJQUF0QixDQUZLLEVBR0w7TUFBRUQsSUFBSSxFQUFFLFlBQVI7TUFBc0JDLElBQUksRUFBRTtJQUE1QixDQUhLLEVBSUw7TUFBRUQsSUFBSSxFQUFFLFFBQVI7TUFBa0JDLElBQUksRUFBRSxRQUF4QjtNQUFrQ0MsU0FBUyxFQUFFLEtBQTdDO01BQW9EQyxVQUFVLEVBQUU7SUFBaEUsQ0FKSztFQUo0QixDQUF6QztBQVdELENBWkEsQ0FBRCJ9\n//# sourceURL=webpack-internal:///./resources/js/warehouse_management/product_categories/index.js\n");
/***/ })
/******/ });
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module can't be inlined because the eval-source-map devtool is used.
/******/ var __webpack_exports__ = {};
/******/ __webpack_modules__["./resources/js/warehouse_management/product_categories/index.js"]();
/******/
/******/ })()
;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,6 @@
{ {
"/js/app.js": "/js/app.js", "/js/app.js": "/js/app.js",
"/js/warehouse_management/product_categories/index.js": "/js/warehouse_management/product_categories/index.js",
"/js/warehouse_management/products/index.js": "/js/warehouse_management/products/index.js",
"/css/app.css": "/css/app.css" "/css/app.css": "/css/app.css"
} }

View File

@@ -0,0 +1,109 @@
$.ajaxSetup({
headers: {
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
},
});
let tableContainer = $("#product-categories-table");
let url = tableContainer.data("url");
let table = $("#product-categories-table").DataTable({
processing: true,
serverSide: true,
ajax: url,
columns: [
{
data: "DT_RowIndex",
name: "DT_RowIndex",
orderable: false,
searchable: false,
},
{ data: "name", name: "name" },
{ data: "action", name: "action", orderable: false, searchable: false },
],
});
$(document).ready(function () {
$("#addProductCategory").click(function () {
$("#productCategoryForm")[0].reset();
$("#category_id").val("");
$("#modalTitle").text("Tambah Kategori");
$("#productCategoryModal").modal("show");
});
// Submit form (baik tambah maupun edit)
$("#productCategoryForm").submit(function (e) {
e.preventDefault();
let id = $("#category_id").val();
let url = id
? `/warehouse/product_categories/${id}`
: `/warehouse/product_categories`;
let method = id ? "PUT" : "POST";
$.ajax({
url: url,
method: method,
data: {
name: $("#name").val(),
_token: $('meta[name="csrf-token"]').attr("content"),
...(id && { _method: "PUT" }),
},
success: function () {
$("#productCategoryModal").modal("hide");
$("#product-categories-table").DataTable().ajax.reload();
},
error: function (xhr) {
alert("Gagal menyimpan data");
console.error(xhr.responseText);
},
});
});
});
$(document).on("click", ".btn-edit-product-category", function () {
const id = $(this).data("id");
const url = $(this).data("url");
$.ajax({
url: url,
method: "GET",
success: function (response) {
$("#category_id").val(response.id);
$("#name").val(response.name);
$("#modalTitle").text("Edit Kategori");
$("#productCategoryModal").modal("show");
},
error: function (xhr) {
alert("Gagal mengambil data");
console.error(xhr.responseText);
},
});
});
$(document).on("click", ".btn-destroy-product-category", function () {
Swal.fire({
title: "Hapus nama kategori?",
text: "Anda tidak akan bisa mengembalikannya!",
showCancelButton: true,
confirmButtonColor: "#d33",
cancelButtonColor: "#dedede",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.value) {
const url = $(this).data("action");
$.ajax({
url: url,
method: "POST",
data: {
_method: "DELETE",
_token: $('meta[name="csrf-token"]').attr("content"),
},
success: function () {
alert("Kategori berhasil dihapus.");
$("#product-categories-table").DataTable().ajax.reload();
},
error: function (xhr) {
alert("Gagal menghapus kategori.");
console.error(xhr.responseText);
},
});
}
});
});

View File

@@ -0,0 +1,112 @@
$.ajaxSetup({
headers: {
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
},
});
let tableContainer = $("#products-table");
let url = tableContainer.data("url");
let table = $("#products-table").DataTable({
processing: true,
serverSide: true,
ajax: url,
columns: [
{
data: "DT_RowIndex",
name: "DT_RowIndex",
orderable: false,
searchable: false,
},
{ data: "code", name: "code" },
{ data: "name", name: "name" },
{ data: "category_name", name: "category.name" },
{ data: "total_stock", name: "total_stock" },
{ data: "action", name: "action", orderable: false, searchable: false },
],
});
$(document).ready(function () {
$("#addProductCategory").click(function () {
$("#productCategoryForm")[0].reset();
$("#category_id").val("");
$("#modalTitle").text("Tambah Kategori");
$("#productCategoryModal").modal("show");
});
// Submit form (baik tambah maupun edit)
$("#productCategoryForm").submit(function (e) {
e.preventDefault();
let id = $("#category_id").val();
let url = id
? `/warehouse/product_categories/${id}`
: `/warehouse/product_categories`;
let method = id ? "PUT" : "POST";
$.ajax({
url: url,
method: method,
data: {
name: $("#name").val(),
_token: $('meta[name="csrf-token"]').attr("content"),
...(id && { _method: "PUT" }),
},
success: function () {
$("#productCategoryModal").modal("hide");
$("#product-categories-table").DataTable().ajax.reload();
},
error: function (xhr) {
alert("Gagal menyimpan data");
console.error(xhr.responseText);
},
});
});
});
$(document).on("click", ".btn-edit-product-category", function () {
const id = $(this).data("id");
const url = $(this).data("url");
$.ajax({
url: url,
method: "GET",
success: function (response) {
$("#category_id").val(response.id);
$("#name").val(response.name);
$("#modalTitle").text("Edit Kategori");
$("#productCategoryModal").modal("show");
},
error: function (xhr) {
alert("Gagal mengambil data");
console.error(xhr.responseText);
},
});
});
$(document).on("click", ".btn-destroy-product-category", function () {
Swal.fire({
title: "Hapus nama kategori?",
text: "Anda tidak akan bisa mengembalikannya!",
showCancelButton: true,
confirmButtonColor: "#d33",
cancelButtonColor: "#dedede",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.value) {
const url = $(this).data("action");
$.ajax({
url: url,
method: "POST",
data: {
_method: "DELETE",
_token: $('meta[name="csrf-token"]').attr("content"),
},
success: function () {
alert("Kategori berhasil dihapus.");
$("#product-categories-table").DataTable().ajax.reload();
},
error: function (xhr) {
alert("Gagal menghapus kategori.");
console.error(xhr.responseText);
},
});
}
});
});

View File

@@ -114,7 +114,7 @@ License: You must have a valid license purchased only from themeforest(the above
</div> </div>
<!-- end::Scrolltop --> <!-- end::Scrolltop -->
<script src="{{ mix('js/app.js') }}"></script>
@yield('javascripts') @yield('javascripts')
</body> </body>
<!-- end::Body --> <!-- end::Body -->

View File

@@ -130,6 +130,160 @@
</div> </div>
</li> </li>
@endif @endif
{{-- Section Header --}}
<div class="kt-menu__section" style="padding: 10px 20px; font-weight: bold;">
<i class="kt-menu__section-icon fa fa-box"></i>
<span class="kt-menu__section-text">Manajemen Pengguna</span>
</div>
{{-- Submenu Items --}}
@can('view', $menus['dealer.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('dealer.index') }}" class="kt-menu__link">
<i class="fa fa-car" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Pengguna</span>
</a>
</li>
@endcan
@can('view', $menus['category.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('category.index') }}" class="kt-menu__link">
<i class="fa fa-users" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Role & Privileges</span>
</a>
</li>
@endcan
{{-- Section Header --}}
<div class="kt-menu__section" style="padding: 10px 20px; font-weight: bold;">
<i class="kt-menu__section-icon fa fa-box"></i>
<span class="kt-menu__section-text">Manajemen Transaksi</span>
</div>
{{-- Submenu Items --}}
@can('view', $menus['dealer.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('dealer.index') }}" class="kt-menu__link">
<i class="fa fa-car" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Pekerjaan</span>
</a>
</li>
@endcan
@can('view', $menus['category.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('category.index') }}" class="kt-menu__link">
<i class="fa fa-users" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Kategori Pekerjaan</span>
</a>
</li>
@endcan
@can('view', $menus['work.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('work.index') }}" class="kt-menu__link">
<i class="fa fa-list" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Dealer</span>
</a>
</li>
@endcan
{{-- Section Header --}}
<div class="kt-menu__section" style="padding: 10px 20px; font-weight: bold;">
<i class="kt-menu__section-icon fa fa-box"></i>
<span class="kt-menu__section-text">Manajemen Gudang</span>
</div>
{{-- Submenu Items --}}
@can('view', $menus['products.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('products.index') }}" class="kt-menu__link">
<i class="fa fa-car" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Produk</span>
</a>
</li>
@endcan
@can('view', $menus['product_categories.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('product_categories.index') }}" class="kt-menu__link">
<i class="fa fa-users" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Kategori Produk</span>
</a>
</li>
@endcan
@can('view', $menus['work.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('work.index') }}" class="kt-menu__link">
<i class="fa fa-list" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Mutasi Produk</span>
</a>
</li>
@endcan
@can('view', $menus['work.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('work.index') }}" class="kt-menu__link">
<i class="fa fa-list" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Stock Opname</span>
</a>
</li>
@endcan
{{-- Section Header --}}
<div class="kt-menu__section" style="padding: 10px 20px; font-weight: bold;">
<i class="kt-menu__section-icon fa fa-box"></i>
<span class="kt-menu__section-text">Laporan</span>
</div>
{{-- Submenu Items --}}
@can('view', $menus['dealer.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('dealer.index') }}" class="kt-menu__link">
<i class="fa fa-car" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">SA</span>
</a>
</li>
@endcan
@can('view', $menus['category.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('category.index') }}" class="kt-menu__link">
<i class="fa fa-users" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Pekerjaan</span>
</a>
</li>
@endcan
@can('view', $menus['work.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('work.index') }}" class="kt-menu__link">
<i class="fa fa-list" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Dealer</span>
</a>
</li>
@endcan
@can('view', $menus['work.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('work.index') }}" class="kt-menu__link">
<i class="fa fa-list" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Stok Produk</span>
</a>
</li>
@endcan
@can('view', $menus['work.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('work.index') }}" class="kt-menu__link">
<i class="fa fa-list" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Teknisi</span>
</a>
</li>
@endcan
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,73 @@
@extends('layouts.backapp')
@section('content')
<div class="kt-portlet kt-portlet--mobile" id="kt_blockui_datatable">
<div class="kt-portlet__head kt-portlet__head--lg">
<div class="kt-portlet__head-label">
<span class="kt-portlet__head-icon">
<i class="kt-font-brand flaticon2-line-chart"></i>
</span>
<h3 class="kt-portlet__head-title">
Kategori Produk
</h3>
</div>
@can('create', $menus['product_categories.index'])
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-wrapper">
<div class="kt-portlet__head-actions">
<button type="button" class="btn btn-bold btn-label-brand btn-sm" id="addProductCategory"> Tambah </button>
</div>
</div>
</div>
@endcan
</div>
<div class="kt-portlet__body">
<div class="table-responsive">
<!--begin: Datatable -->
<table class="table table-striped table-bordered table-hover" id="product-categories-table" data-url="{{ route("product_categories.index") }}">
<thead>
<tr>
<th>NO</th>
<th>Nama Kategori</th>
<th>Aksi</th>
</tr>
</thead>
</table>
<!--end: Datatable -->
</div>
</div>
</div>
{{-- begin modal --}}
<div class="modal fade" id="productCategoryModal" tabindex="-1" aria-labelledby="modalTitle" aria-hidden="true">
<div class="modal-dialog">
<form id="productCategoryForm">
@csrf
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalTitle">Tambah Kategori</h5>
<button type="button" class="close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="hidden" id="category_id">
<div class="mb-3">
<label for="name" class="form-label">Nama Kategori</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" id="saveBtn">Simpan</button>
</div>
</div>
</form>
</div>
</div>
{{-- end modal --}}
@endsection
@section('javascripts')
<script src="{{mix('js/warehouse_management/product_categories/index.js')}}"></script>
@endsection

View File

@@ -0,0 +1,37 @@
@extends('layouts.backapp')
@section('content')
<div class="kt-portlet kt-portlet--mobile" id="kt_blockui_datatable">
<div class="kt-portlet__head kt-portlet__head--lg">
<div class="kt-portlet__head-label">
<span class="kt-portlet__head-icon">
<i class="kt-font-brand flaticon2-line-chart"></i>
</span>
<h3 class="kt-portlet__head-title">
Tambah Produk
</h3>
</div>
</div>
<div class="kt-portlet__body">
<div class="">
</div>
<div class="table-responsive">
<!--begin: Datatable -->
<table class="table table-striped table-bordered table-hover" id="stock-dealers-table" data-url="{{ route("products.index") }}">
<thead>
<tr>
<th>NO</th>
<th>Kode</th>
<th>Nama</th>
<th>Address</th>
<th>Stok</th>
</tr>
</thead>
</table>
<!--end: Datatable -->
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,48 @@
@extends('layouts.backapp')
@section('content')
<div class="kt-portlet kt-portlet--mobile" id="kt_blockui_datatable">
<div class="kt-portlet__head kt-portlet__head--lg">
<div class="kt-portlet__head-label">
<span class="kt-portlet__head-icon">
<i class="kt-font-brand flaticon2-line-chart"></i>
</span>
<h3 class="kt-portlet__head-title">
Produk
</h3>
</div>
@can('create', $menus['product_categories.index'])
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-wrapper">
<div class="kt-portlet__head-actions">
<button type="button" class="btn btn-bold btn-label-brand btn-sm" id="addProductCategory"> Tambah </button>
</div>
</div>
</div>
@endcan
</div>
<div class="kt-portlet__body">
<div class="table-responsive">
<!--begin: Datatable -->
<table class="table table-striped table-bordered table-hover" id="products-table" data-url="{{ route("products.index") }}">
<thead>
<tr>
<th>NO</th>
<th>Kode</th>
<th>Nama</th>
<th>Kategori</th>
<th>Stok</th>
<th>Aksi</th>
</tr>
</thead>
</table>
<!--end: Datatable -->
</div>
</div>
</div>
@endsection
@section('javascripts')
<script src="{{mix('js/warehouse_management/products/index.js')}}"></script>
@endsection

View File

@@ -7,12 +7,16 @@ use App\Http\Controllers\ReportController;
use App\Http\Controllers\RolePrivilegeController; use App\Http\Controllers\RolePrivilegeController;
use App\Http\Controllers\TransactionController; use App\Http\Controllers\TransactionController;
use App\Http\Controllers\UserController; use App\Http\Controllers\UserController;
use App\Http\Controllers\WarehouseManagement\ProductCategoriesController;
use App\Http\Controllers\WarehouseManagement\ProductsController;
use App\Http\Controllers\WorkController; use App\Http\Controllers\WorkController;
use App\Models\Menu; use App\Models\Menu;
use App\Models\Privilege; use App\Models\Privilege;
use App\Models\Role; use App\Models\Role;
use App\Models\User; use App\Models\User;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Auth;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@@ -26,7 +30,7 @@ use Illuminate\Support\Facades\Route;
*/ */
Route::get('/clear', function() { Route::get('/clear', function() {
\Artisan::call('cache:clear'); Artisan::call('cache:clear');
}); });
Route::get('delete_role_privileges', function() { Route::get('delete_role_privileges', function() {
@@ -198,6 +202,11 @@ Route::group(['middleware' => 'auth'], function() {
Route::get('/report/transaction_sa/export', [ReportController::class, 'sa_export'])->name('report.transaction_sa.export'); Route::get('/report/transaction_sa/export', [ReportController::class, 'sa_export'])->name('report.transaction_sa.export');
Route::get('/report/transaction_dealer', [ReportController::class, 'transaction_dealer'])->name('report.transaction_dealer'); Route::get('/report/transaction_dealer', [ReportController::class, 'transaction_dealer'])->name('report.transaction_dealer');
}); });
Route::prefix('warehouse')->group(function (){
Route::resource('products', ProductsController::class);
Route::resource('product_categories', ProductCategoriesController::class);
});
}); });

View File

@@ -1,4 +1,4 @@
const mix = require('laravel-mix'); const mix = require("laravel-mix");
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@@ -11,6 +11,14 @@ const mix = require('laravel-mix');
| |
*/ */
mix.js('resources/js/app.js', 'public/js') mix.js("resources/js/app.js", "public/js")
.sass('resources/sass/app.scss', 'public/css') .sass("resources/sass/app.scss", "public/css")
.js(
"resources/js/warehouse_management/product_categories/index.js",
"public/js/warehouse_management/product_categories"
)
.js(
"resources/js/warehouse_management/products/index.js",
"public/js/warehouse_management/products"
)
.sourceMaps(); .sourceMaps();