partial update detail opnames page

This commit is contained in:
2025-06-05 15:18:20 +07:00
parent ce0a4718e0
commit d294bb7876
15 changed files with 217 additions and 72 deletions

View File

@@ -8,6 +8,7 @@ use App\Models\Menu;
use App\Models\Opname;
use App\Models\OpnameDetail;
use App\Models\Product;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
@@ -20,17 +21,19 @@ class OpnamesController extends Controller
if($request->ajax()){
$data = Opname::with('user','dealer');
return DataTables::of($data)
->addIndexColumn()
->addColumn('user_name', function ($row){
return $row->user ? $row->user->name : '-';
})
->addColumn('dealer_name', function ($row){
return $row->dealer ? $row->dealer->name : '-';
})
->editColumn('opname_date', function ($row){
return $row->opname_date ? Carbon::parse($row->opname_date)->format('d M Y') : '-';
})
->addColumn('action', function ($row) use ($menu) {
$btn = '<div class="d-flex">';
$btn .= '<button class="btn btn-sm btn-secondary btn-product-stock-dealers">Detail</button>';
$btn .= '<a href="'.route('opnames.show', $row->id).'" class="btn btn-secondary btn-sm">Detail</a>';
$btn .= '</div>';
@@ -94,4 +97,41 @@ class OpnamesController extends Controller
Log::error($ex->getMessage());
}
}
public function show(Request $request, $id)
{
try {
$opname = Opname::with('details.product', 'user')->findOrFail($id);
if ($request->ajax()) {
return DataTables::of($opname->details)
->addIndexColumn()
->addColumn('opname_date', function () use ($opname) {
return $opname->opname_date->format('d M Y');
})
->addColumn('user_name', function () use ($opname) {
return $opname->user ? $opname->user->name : '-';
})
->addColumn('product_name', function ($detail) {
return $detail->product->name ?? '-';
})
->addColumn('system_stock', function ($detail) {
return $detail->system_stock;
})
->addColumn('physical_stock', function ($detail) {
return $detail->physical_stock;
})
->addColumn('difference', function ($detail) {
return $detail->difference;
})
->make(true);
}
return view('warehouse_management.opnames.detail', compact('opname'));
} catch (\Exception $ex) {
Log::error($ex->getMessage());
abort(500, 'Something went wrong');
}
}
}

File diff suppressed because one or more lines are too long

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/opnames/detail.js":
/*!*************************************************************!*\
!*** ./resources/js/warehouse_management/opnames/detail.js ***!
\*************************************************************/
/***/ (() => {
eval("$.ajaxSetup({\n headers: {\n \"X-CSRF-TOKEN\": $('meta[name=\"csrf-token\"]').attr(\"content\")\n }\n});\nvar tableContainer = $(\"#opname-detail-table\");\nvar url = tableContainer.data(\"url\");\nvar table = $(\"#opname-detail-table\").DataTable({\n processing: true,\n serverSide: true,\n ajax: url,\n columns: [{\n data: \"opname_date\",\n name: \"opname_date\"\n }, {\n data: \"user_name\",\n name: \"user.name\"\n }, {\n data: \"product_name\",\n name: \"product.name\"\n }, {\n data: \"system_stock\",\n name: \"system_stock\"\n }, {\n data: \"physical_stock\",\n name: \"physical_stock\"\n }, {\n data: \"difference\",\n name: \"difference\"\n }]\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyIkIiwiYWpheFNldHVwIiwiaGVhZGVycyIsImF0dHIiLCJ0YWJsZUNvbnRhaW5lciIsInVybCIsImRhdGEiLCJ0YWJsZSIsIkRhdGFUYWJsZSIsInByb2Nlc3NpbmciLCJzZXJ2ZXJTaWRlIiwiYWpheCIsImNvbHVtbnMiLCJuYW1lIl0sInNvdXJjZXMiOlsid2VicGFjazovLy8uL3Jlc291cmNlcy9qcy93YXJlaG91c2VfbWFuYWdlbWVudC9vcG5hbWVzL2RldGFpbC5qcz9mMzY3Il0sInNvdXJjZXNDb250ZW50IjpbIiQuYWpheFNldHVwKHtcbiAgICBoZWFkZXJzOiB7XG4gICAgICAgIFwiWC1DU1JGLVRPS0VOXCI6ICQoJ21ldGFbbmFtZT1cImNzcmYtdG9rZW5cIl0nKS5hdHRyKFwiY29udGVudFwiKSxcbiAgICB9LFxufSk7XG5sZXQgdGFibGVDb250YWluZXIgPSAkKFwiI29wbmFtZS1kZXRhaWwtdGFibGVcIik7XG5sZXQgdXJsID0gdGFibGVDb250YWluZXIuZGF0YShcInVybFwiKTtcbmxldCB0YWJsZSA9ICQoXCIjb3BuYW1lLWRldGFpbC10YWJsZVwiKS5EYXRhVGFibGUoe1xuICAgIHByb2Nlc3Npbmc6IHRydWUsXG4gICAgc2VydmVyU2lkZTogdHJ1ZSxcbiAgICBhamF4OiB1cmwsXG4gICAgY29sdW1uczogW1xuICAgICAgICB7IGRhdGE6IFwib3BuYW1lX2RhdGVcIiwgbmFtZTogXCJvcG5hbWVfZGF0ZVwiIH0sXG4gICAgICAgIHsgZGF0YTogXCJ1c2VyX25hbWVcIiwgbmFtZTogXCJ1c2VyLm5hbWVcIiB9LFxuICAgICAgICB7IGRhdGE6IFwicHJvZHVjdF9uYW1lXCIsIG5hbWU6IFwicHJvZHVjdC5uYW1lXCIgfSxcbiAgICAgICAgeyBkYXRhOiBcInN5c3RlbV9zdG9ja1wiLCBuYW1lOiBcInN5c3RlbV9zdG9ja1wiIH0sXG4gICAgICAgIHsgZGF0YTogXCJwaHlzaWNhbF9zdG9ja1wiLCBuYW1lOiBcInBoeXNpY2FsX3N0b2NrXCIgfSxcbiAgICAgICAgeyBkYXRhOiBcImRpZmZlcmVuY2VcIiwgbmFtZTogXCJkaWZmZXJlbmNlXCIgfSxcbiAgICBdLFxufSk7XG4iXSwibWFwcGluZ3MiOiJBQUFBQSxDQUFDLENBQUNDLFNBQUYsQ0FBWTtFQUNSQyxPQUFPLEVBQUU7SUFDTCxnQkFBZ0JGLENBQUMsQ0FBQyx5QkFBRCxDQUFELENBQTZCRyxJQUE3QixDQUFrQyxTQUFsQztFQURYO0FBREQsQ0FBWjtBQUtBLElBQUlDLGNBQWMsR0FBR0osQ0FBQyxDQUFDLHNCQUFELENBQXRCO0FBQ0EsSUFBSUssR0FBRyxHQUFHRCxjQUFjLENBQUNFLElBQWYsQ0FBb0IsS0FBcEIsQ0FBVjtBQUNBLElBQUlDLEtBQUssR0FBR1AsQ0FBQyxDQUFDLHNCQUFELENBQUQsQ0FBMEJRLFNBQTFCLENBQW9DO0VBQzVDQyxVQUFVLEVBQUUsSUFEZ0M7RUFFNUNDLFVBQVUsRUFBRSxJQUZnQztFQUc1Q0MsSUFBSSxFQUFFTixHQUhzQztFQUk1Q08sT0FBTyxFQUFFLENBQ0w7SUFBRU4sSUFBSSxFQUFFLGFBQVI7SUFBdUJPLElBQUksRUFBRTtFQUE3QixDQURLLEVBRUw7SUFBRVAsSUFBSSxFQUFFLFdBQVI7SUFBcUJPLElBQUksRUFBRTtFQUEzQixDQUZLLEVBR0w7SUFBRVAsSUFBSSxFQUFFLGNBQVI7SUFBd0JPLElBQUksRUFBRTtFQUE5QixDQUhLLEVBSUw7SUFBRVAsSUFBSSxFQUFFLGNBQVI7SUFBd0JPLElBQUksRUFBRTtFQUE5QixDQUpLLEVBS0w7SUFBRVAsSUFBSSxFQUFFLGdCQUFSO0lBQTBCTyxJQUFJLEVBQUU7RUFBaEMsQ0FMSyxFQU1MO0lBQUVQLElBQUksRUFBRSxZQUFSO0lBQXNCTyxJQUFJLEVBQUU7RUFBNUIsQ0FOSztBQUptQyxDQUFwQyxDQUFaIiwiZmlsZSI6Ii4vcmVzb3VyY2VzL2pzL3dhcmVob3VzZV9tYW5hZ2VtZW50L29wbmFtZXMvZGV0YWlsLmpzIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./resources/js/warehouse_management/opnames/detail.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/opnames/detail.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

@@ -6,5 +6,6 @@
"/js/warehouse_management/products/edit.js": "/js/warehouse_management/products/edit.js",
"/js/warehouse_management/opnames/index.js": "/js/warehouse_management/opnames/index.js",
"/js/warehouse_management/opnames/create.js": "/js/warehouse_management/opnames/create.js",
"/js/warehouse_management/opnames/detail.js": "/js/warehouse_management/opnames/detail.js",
"/css/app.css": "/css/app.css"
}

View File

@@ -0,0 +1,20 @@
$.ajaxSetup({
headers: {
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
},
});
let tableContainer = $("#opname-detail-table");
let url = tableContainer.data("url");
let table = $("#opname-detail-table").DataTable({
processing: true,
serverSide: true,
ajax: url,
columns: [
{ data: "opname_date", name: "opname_date" },
{ data: "user_name", name: "user.name" },
{ data: "product_name", name: "product.name" },
{ data: "system_stock", name: "system_stock" },
{ data: "physical_stock", name: "physical_stock" },
{ data: "difference", name: "difference" },
],
});

View File

@@ -16,34 +16,3 @@ let table = $("#opnames-table").DataTable({
{ data: "action", name: "action", orderable: false, searchable: false },
],
});
$(document).on("click", ".btn-product-stock-dealers", function () {
const productId = $(this).data("id");
const productName = $(this).data("name");
const ajaxUrl = $(this).data("url");
// Set product name in modal title
$("#product-name-title").text(productName);
// Initialize or reload DataTable inside modal
$("#dealer-stock-table").DataTable({
destroy: true, // reinit if exists
processing: true,
serverSide: true,
ajax: {
url: ajaxUrl,
data: {
product_id: productId,
},
},
columns: [
{ data: "dealer_name", name: "dealer_name" },
{ data: "system_stock", name: "system_stock" },
{ data: "physical_stock", name: "physical_stock" },
{ data: "difference", name: "difference" },
{ data: "opname_date", name: "opname_date" },
],
});
// Show the modal
$("#dealerStockModal").modal("show");
});

View File

@@ -14,7 +14,12 @@ let table = $("#products-table").DataTable({
{ data: "name", name: "name" },
{ data: "category_name", name: "category.name" },
{ data: "unit", name: "unit" },
{ data: "total_stock", name: "total_stock" },
{
data: "total_stock",
name: "total_stock",
orderable: false,
searchable: false,
},
{ data: "action", name: "action", orderable: false, searchable: false },
],
});
@@ -83,3 +88,37 @@ $(document).on("click", ".btn-toggle-active", function () {
}
});
});
$(document).on("click", ".btn-product-stock-dealers", function () {
const productId = $(this).data("id");
const productName = $(this).data("name");
const ajaxUrl = $(this).data("url");
// Set product name in modal title
$("#product-name-title").text(productName);
// Initialize or reload DataTable inside modal
$("#dealer-stock-table").DataTable({
destroy: true, // reinit if exists
processing: true,
serverSide: true,
ajax: {
url: ajaxUrl,
data: {
product_id: productId,
},
},
columns: [
{ data: "dealer_name", name: "dealer_name" },
{ data: "system_stock", name: "system_stock" },
{ data: "physical_stock", name: "physical_stock" },
{ data: "difference", name: "difference" },
{ data: "opname_date", name: "opname_date" },
],
initComplete: function () {
$("#dealerStockModal").modal("show");
},
});
});
$(document).on("click", "#dealerStockModal .close", function () {
$("#dealerStockModal").modal("hide");
});

View File

@@ -16,7 +16,7 @@
@csrf
<div class="form-group">
<label for="dealer">Dealer</label>
<select name="dealer" id="dealer" class="form-control">
<select name="dealer" id="dealer" class="form-control" required >
<option value="">Pilih Dealer</option>
@foreach($dealers as $dealer)
<option value="{{ $dealer->id }}">{{ $dealer->name }}</option>
@@ -28,17 +28,17 @@
<div class="form-row align-items-end product-row">
<div class="form-group col-md-4">
<label for="product[]">Produk</label>
<select name="product[]" class="form-control product-select" >
<select name="product[]" class="form-control product-select" required >
<option value="">Pilih Produk</option>
</select>
</div>
<div class="form-group col-md-3">
<label>Stok Sistem</label>
<input type="text" name="system_quantity[]" class="form-control" placeholder="Stok sistem">
<input type="text" name="system_quantity[]" class="form-control" placeholder="Stok sistem" required >
</div>
<div class="form-group col-md-3">
<label>Stok Fisik</label>
<input type="text" name="physical_quantity[]" class="form-control" placeholder="Stok fisik">
<input type="text" name="physical_quantity[]" class="form-control" placeholder="Stok fisik" required >
</div>
<div class="form-group col-md-2">
<button type="button" class="btn btn-success btn-add-row"><i class="flaticon2-plus"></i></button>

View File

@@ -0,0 +1,39 @@
@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-list-1"></i>
</span>
<h3 class="kt-portlet__head-title">
Detail Opname
</h3>
</div>
</div>
<div class="kt-portlet__body">
<div class="table-responsive">
<!--begin: Datatable -->
<table class="table table-striped table-bordered table-hover" id="opname-detail-table" data-url="{{ route("opnames.show", $opname->id) }}">
<thead>
<tr>
<th>Tanggal</th>
<th>Pengguna</th>
<th>Produk</th>
<th>Sistem</th>
<th>Fisik</th>
<th>Selisih</th>
</tr>
</thead>
</table>
<!--end: Datatable -->
</div>
</div>
</div>
@endsection
@section('javascripts')
<script src="{{ mix('js/warehouse_management/opnames/detail.js') }}"></script>
@endsection

View File

@@ -11,7 +11,7 @@
Tabel Opnames
</h3>
</div>
@can('create', $menus['product_categories.index'])
@can('create', $menus['opnames.index'])
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-wrapper">
<div class="kt-portlet__head-actions">
@@ -38,35 +38,7 @@
<!--end: Datatable -->
</div>
</div>
</div>
<div class="modal fade" id="dealerStockModal" tabindex="-1" role="dialog" aria-labelledby="dealerStockModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Dealer Stock for <span id="product-name-title"></span></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<table id="dealer-stock-table" class="table table-bordered">
<thead>
<tr>
<th>Dealer</th>
<th>System Stock</th>
<th>Physical Stock</th>
<th>Difference</th>
<th>Opname Date</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
@section('javascripts')

View File

@@ -11,7 +11,7 @@
Tabel Produk
</h3>
</div>
@can('create', $menus['product_categories.index'])
@can('create', $menus['products.index'])
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-wrapper">
<div class="kt-portlet__head-actions">
@@ -41,6 +41,34 @@
</div>
</div>
</div>
{{-- modal dealer stock --}}
<div class="modal fade" id="dealerStockModal" tabindex="-1" role="dialog" aria-labelledby="dealerStockModalLabel" aria-hidden="true">
<div class="modal-dialog pl-5 modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Dealer Stock for <span id="product-name-title"></span></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<table id="dealer-stock-table" class="table table-bordered">
<thead>
<tr>
<th>Dealer</th>
<th>System Stock</th>
<th>Physical Stock</th>
<th>Difference</th>
<th>Opname Date</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
@section('javascripts')

View File

@@ -212,7 +212,7 @@ Route::group(['middleware' => 'auth'], function() {
Route::get('create', 'create')->name('products.create');
Route::post('/', 'store')->name('products.store');
Route::get('all','all_products')->name('products.all');
Route::get('dealers-stock')->name('products.dealers_stock');
Route::get('dealers-stock', 'dealers_stock')->name('products.dealers_stock');
Route::get('{product}', 'show')->name('products.show');
Route::get('{product}/edit', 'edit')->name('products.edit');
Route::put('{product}', 'update')->name('products.update');
@@ -236,6 +236,7 @@ Route::group(['middleware' => 'auth'], function() {
Route::get('/','index')->name('opnames.index');
Route::get('create','create')->name('opnames.create');
Route::post('/','store')->name('opnames.store');
Route::get('{opnames}','show')->name('opnames.show');
});
});
});

View File

@@ -37,6 +37,10 @@ mix.js("resources/js/app.js", "public/js")
"resources/js/warehouse_management/opnames/create.js",
"public/js/warehouse_management/opnames"
)
.js(
"resources/js/warehouse_management/opnames/detail.js",
"public/js/warehouse_management/opnames"
)
.sourceMaps();
mix.browserSync({