fix opname default value, show different opname and hide system stock opname
This commit is contained in:
@@ -27,7 +27,7 @@ class OpnamesController extends Controller
|
|||||||
$dealers = Dealer::all();
|
$dealers = Dealer::all();
|
||||||
if($request->ajax()){
|
if($request->ajax()){
|
||||||
$data = Opname::query()
|
$data = Opname::query()
|
||||||
->with('user','dealer')
|
->with(['user','dealer', 'details.product'])
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('created_at', 'desc');
|
||||||
|
|
||||||
// Filter berdasarkan dealer yang dipilih
|
// Filter berdasarkan dealer yang dipilih
|
||||||
@@ -76,6 +76,46 @@ class OpnamesController extends Controller
|
|||||||
|
|
||||||
return "<span class=\"font-weight-bold {$textColorClass}\">{$label}</span>";
|
return "<span class=\"font-weight-bold {$textColorClass}\">{$label}</span>";
|
||||||
})
|
})
|
||||||
|
->addColumn('stock_info', function ($row) {
|
||||||
|
// Use eager loaded details
|
||||||
|
$details = $row->details;
|
||||||
|
|
||||||
|
if ($details->isEmpty()) {
|
||||||
|
return '<span class="text-muted">Tidak ada data</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalProducts = $details->count();
|
||||||
|
$matchingProducts = $details->where('difference', 0)->count();
|
||||||
|
$differentProducts = $totalProducts - $matchingProducts;
|
||||||
|
|
||||||
|
$info = [];
|
||||||
|
|
||||||
|
if ($matchingProducts > 0) {
|
||||||
|
$info[] = "<span class='text-success'><i class='fa fa-check-circle'></i> {$matchingProducts} sesuai</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($differentProducts > 0) {
|
||||||
|
// Get more details about differences
|
||||||
|
$positiveDiff = $details->where('difference', '>', 0)->count();
|
||||||
|
$negativeDiff = $details->where('difference', '<', 0)->count();
|
||||||
|
|
||||||
|
$diffInfo = [];
|
||||||
|
if ($positiveDiff > 0) {
|
||||||
|
$diffInfo[] = "+{$positiveDiff}";
|
||||||
|
}
|
||||||
|
if ($negativeDiff > 0) {
|
||||||
|
$diffInfo[] = "-{$negativeDiff}";
|
||||||
|
}
|
||||||
|
|
||||||
|
$diffText = implode(', ', $diffInfo);
|
||||||
|
$info[] = "<span class='text-danger'><i class='fa fa-exclamation-triangle'></i> {$differentProducts} selisih ({$diffText})</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add total products info
|
||||||
|
$info[] = "<small class='text-muted'>(Total: {$totalProducts} produk)</small>";
|
||||||
|
|
||||||
|
return '<div class="stock-info-cell">' . implode('<br>', $info) . '</div>';
|
||||||
|
})
|
||||||
->addColumn('action', function ($row) use ($menu) {
|
->addColumn('action', function ($row) use ($menu) {
|
||||||
$btn = '<div class="d-flex">';
|
$btn = '<div class="d-flex">';
|
||||||
|
|
||||||
@@ -86,7 +126,7 @@ class OpnamesController extends Controller
|
|||||||
|
|
||||||
return $btn;
|
return $btn;
|
||||||
})
|
})
|
||||||
->rawColumns(['action', 'status'])
|
->rawColumns(['action', 'status', 'stock_info'])
|
||||||
->make(true);
|
->make(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +164,7 @@ class OpnamesController extends Controller
|
|||||||
$isTransactionForm = $request->has('form') && $request->form === 'opname';
|
$isTransactionForm = $request->has('form') && $request->form === 'opname';
|
||||||
|
|
||||||
if ($isTransactionForm) {
|
if ($isTransactionForm) {
|
||||||
// Custom validation for transaction form
|
// Simplified validation for transaction form
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'dealer_id' => 'required|exists:dealers,id',
|
'dealer_id' => 'required|exists:dealers,id',
|
||||||
'user_id' => 'required|exists:users,id',
|
'user_id' => 'required|exists:users,id',
|
||||||
@@ -140,7 +180,7 @@ class OpnamesController extends Controller
|
|||||||
'system_stock' => 'required|array',
|
'system_stock' => 'required|array',
|
||||||
'system_stock.*' => 'required|numeric|min:0',
|
'system_stock.*' => 'required|numeric|min:0',
|
||||||
'physical_stock' => 'required|array',
|
'physical_stock' => 'required|array',
|
||||||
'physical_stock.*' => 'required|numeric|min:0'
|
'physical_stock.*' => 'nullable|numeric|min:0'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Process transaction form data with proper date parsing
|
// Process transaction form data with proper date parsing
|
||||||
@@ -199,19 +239,11 @@ class OpnamesController extends Controller
|
|||||||
$physicalStocks = $request->physical_quantity;
|
$physicalStocks = $request->physical_quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Validasi minimal ada produk yang diisi (termasuk nilai 0)
|
// 2. Simplified validation - all products are valid, set defaults for empty physical stocks
|
||||||
$validProductIds = array_filter($productIds);
|
$validProductIds = array_filter($productIds);
|
||||||
$validSystemStocks = array_filter($systemStocks, function($value) { return $value !== null && $value !== ''; });
|
|
||||||
$validPhysicalStocks = array_filter($physicalStocks, function($value) {
|
|
||||||
return $value !== null && $value !== '' && is_numeric($value);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (empty($validProductIds) || count($validProductIds) === 0) {
|
if (empty($validProductIds) || count($validProductIds) === 0) {
|
||||||
throw new \Exception('Minimal harus ada satu produk yang diisi untuk opname.');
|
throw new \Exception('Minimal harus ada satu produk untuk opname.');
|
||||||
}
|
|
||||||
|
|
||||||
if (count($validPhysicalStocks) === 0) {
|
|
||||||
throw new \Exception('Minimal harus ada satu stock fisik yang diisi (termasuk nilai 0).');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Validasi duplikasi produk
|
// 3. Validasi duplikasi produk
|
||||||
@@ -283,19 +315,14 @@ class OpnamesController extends Controller
|
|||||||
foreach ($productIds as $index => $productId) {
|
foreach ($productIds as $index => $productId) {
|
||||||
if (!$productId) continue;
|
if (!$productId) continue;
|
||||||
|
|
||||||
// Skip only if physical stock is truly not provided (empty string or null)
|
// Set default value to 0 if physical stock is empty or invalid
|
||||||
// Accept 0 as valid input
|
$physicalStockValue = $physicalStocks[$index] ?? null;
|
||||||
if (!isset($physicalStocks[$index]) || $physicalStocks[$index] === '' || $physicalStocks[$index] === null) {
|
if ($physicalStockValue === '' || $physicalStockValue === null || !is_numeric($physicalStockValue)) {
|
||||||
continue;
|
$physicalStockValue = 0;
|
||||||
}
|
|
||||||
|
|
||||||
// Validate that physical stock is numeric (including 0)
|
|
||||||
if (!is_numeric($physicalStocks[$index])) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$systemStock = floatval($systemStocks[$index] ?? 0);
|
$systemStock = floatval($systemStocks[$index] ?? 0);
|
||||||
$physicalStock = floatval($physicalStocks[$index]);
|
$physicalStock = floatval($physicalStockValue);
|
||||||
$difference = $physicalStock - $systemStock;
|
$difference = $physicalStock - $systemStock;
|
||||||
|
|
||||||
$processedCount++;
|
$processedCount++;
|
||||||
@@ -337,7 +364,7 @@ class OpnamesController extends Controller
|
|||||||
|
|
||||||
// Validate we have at least one detail to insert
|
// Validate we have at least one detail to insert
|
||||||
if (empty($details)) {
|
if (empty($details)) {
|
||||||
throw new \Exception('Tidak ada data stock fisik yang valid untuk diproses.');
|
throw new \Exception('Tidak ada data produk yang valid untuk diproses.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bulk insert untuk performa lebih baik
|
// Bulk insert untuk performa lebih baik
|
||||||
@@ -371,13 +398,13 @@ class OpnamesController extends Controller
|
|||||||
// Redirect back to transaction page with success message and tab indicator
|
// Redirect back to transaction page with success message and tab indicator
|
||||||
return redirect()
|
return redirect()
|
||||||
->route('transaction')
|
->route('transaction')
|
||||||
->with('success', "Opname berhasil disimpan dan disetujui. {$processedCount} produk telah diproses.")
|
->with('success', "Opname berhasil disimpan. {$processedCount} produk telah diproses.")
|
||||||
->with('active_tab', 'opname');
|
->with('active_tab', 'opname');
|
||||||
} else {
|
} else {
|
||||||
// Redirect to opname index for regular form
|
// Redirect to opname index for regular form
|
||||||
return redirect()
|
return redirect()
|
||||||
->route('opnames.index')
|
->route('opnames.index')
|
||||||
->with('success', "Opname berhasil disimpan dan disetujui. {$processedCount} produk telah diproses.");
|
->with('success', "Opname berhasil disimpan. {$processedCount} produk telah diproses.");
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Illuminate\Validation\ValidationException $e) {
|
} catch (\Illuminate\Validation\ValidationException $e) {
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
(()=>{function e(){$("#date_to").datepicker({format:"yyyy-mm-dd",autoclose:!0,todayHighlight:!0,orientation:"bottom left",templates:{leftArrow:'<i class="la la-angle-left"></i>',rightArrow:'<i class="la la-angle-right"></i>'},endDate:new Date,clearBtn:!0}).on("changeDate",(function(e){console.log("End date selected:",e.format())})).on("clearDate",(function(e){console.log("End date cleared")}))}function a(){$("#date_to").datepicker("remove"),$("#date_to").val(""),e(),$("#date_to").prop("disabled",!0),console.log("End date picker reset and disabled")}$(document).ready((function(){console.log("Opnames index.js loaded"),void 0!==$.fn.DataTable?(console.log("Initializing Select2..."),void 0!==$.fn.select2?$("#dealer_filter").select2({placeholder:"Pilih...",allowClear:!0,width:"100%"}):console.warn("Select2 not available, using regular select"),function(){if(console.log("Initializing datepickers..."),void 0===$.fn.datepicker)return void console.error("Bootstrap Datepicker not available!");$("#date_from").datepicker({format:"yyyy-mm-dd",autoclose:!0,todayHighlight:!0,orientation:"bottom left",templates:{leftArrow:'<i class="la la-angle-left"></i>',rightArrow:'<i class="la la-angle-right"></i>'},endDate:new Date,clearBtn:!0}).on("changeDate",(function(e){var a;console.log("Start date selected:",e.format()),a=e.format(),console.log("Enabling end date picker with min date:",a),$("#date_to").prop("disabled",!1),$("#date_to").datepicker("remove"),$("#date_to").datepicker({format:"yyyy-mm-dd",autoclose:!0,todayHighlight:!0,orientation:"bottom left",templates:{leftArrow:'<i class="la la-angle-left"></i>',rightArrow:'<i class="la la-angle-right"></i>'},startDate:a,endDate:new Date,clearBtn:!0}).on("changeDate",(function(e){console.log("End date selected:",e.format())})).on("clearDate",(function(e){console.log("End date cleared")})),console.log("End date picker enabled with startDate:",a)})).on("clearDate",(function(e){console.log("Start date cleared"),a()})),e(),$("#date_to").prop("disabled",!0)}(),setTimeout((function(){!function(){console.log("Initializing DataTable..."),$.fn.DataTable.isDataTable("#opnames-table")&&$("#opnames-table").DataTable().destroy();var e=$("#opnames-table").DataTable({processing:!0,serverSide:!0,destroy:!0,ajax:{url:$("#opnames-table").data("url"),type:"GET",data:function(e){return e.dealer_filter=$("#dealer_filter").val(),e.date_from=$("#date_from").val(),e.date_to=$("#date_to").val(),console.log("AJAX data being sent:",{dealer_filter:e.dealer_filter,date_from:e.date_from,date_to:e.date_to}),e},error:function(e,a,t){console.error("DataTables AJAX error:",a,t),console.error("Response:",e.responseText)}},columnDefs:[{targets:0,width:"15%"},{targets:5,width:"15%",className:"text-center"}],columns:[{data:"created_at",name:"created_at",orderable:!0},{data:"opname_date",name:"opname_date",orderable:!0},{data:"dealer_name",name:"dealer.name",orderable:!0},{data:"user_name",name:"user.name",orderable:!0},{data:"status",name:"status",orderable:!0},{data:"action",name:"action",orderable:!1,searchable:!1}],order:[[4,"desc"]],pageLength:10,responsive:!0,ordering:!0,orderMulti:!1});(function(e){$("#kt_search").on("click",(function(){console.log("Filter button clicked");var a=$("#dealer_filter").val(),t=$("#date_from").val(),o=$("#date_to").val();console.log("Filtering with:",{dealer:a,dateFrom:t,dateTo:o}),e.ajax.reload()})),$("#kt_reset").on("click",(function(){console.log("Reset button clicked"),$("#dealer_filter").val(null).trigger("change.select2"),$("#date_from").datepicker("clearDates"),$("#date_to").datepicker("clearDates"),a(),e.ajax.reload()})),$("#date_from, #date_to").on("keypress",(function(e){13===e.which&&$("#kt_search").click()})),$("#dealer_filter").on("change",(function(){console.log("Dealer filter changed:",$(this).val())}))})(e),function(e){e.on("order.dt",(function(){console.log("Order changed:",e.order())})),e.on("processing.dt",(function(e,a,t){t?console.log("DataTable processing started"):console.log("DataTable processing finished")}))}(e)}()}),100)):console.error("DataTables not available!")}))})();
|
(()=>{function e(){$("#date_to").datepicker({format:"yyyy-mm-dd",autoclose:!0,todayHighlight:!0,orientation:"bottom left",templates:{leftArrow:'<i class="la la-angle-left"></i>',rightArrow:'<i class="la la-angle-right"></i>'},endDate:new Date,clearBtn:!0}).on("changeDate",(function(e){console.log("End date selected:",e.format())})).on("clearDate",(function(e){console.log("End date cleared")}))}function a(){$("#date_to").datepicker("remove"),$("#date_to").val(""),e(),$("#date_to").prop("disabled",!0),console.log("End date picker reset and disabled")}$(document).ready((function(){console.log("Opnames index.js loaded"),void 0!==$.fn.DataTable?(console.log("Initializing Select2..."),void 0!==$.fn.select2?$("#dealer_filter").select2({placeholder:"Pilih...",allowClear:!0,width:"100%"}):console.warn("Select2 not available, using regular select"),function(){if(console.log("Initializing datepickers..."),void 0===$.fn.datepicker)return void console.error("Bootstrap Datepicker not available!");$("#date_from").datepicker({format:"yyyy-mm-dd",autoclose:!0,todayHighlight:!0,orientation:"bottom left",templates:{leftArrow:'<i class="la la-angle-left"></i>',rightArrow:'<i class="la la-angle-right"></i>'},endDate:new Date,clearBtn:!0}).on("changeDate",(function(e){var a;console.log("Start date selected:",e.format()),a=e.format(),console.log("Enabling end date picker with min date:",a),$("#date_to").prop("disabled",!1),$("#date_to").datepicker("remove"),$("#date_to").datepicker({format:"yyyy-mm-dd",autoclose:!0,todayHighlight:!0,orientation:"bottom left",templates:{leftArrow:'<i class="la la-angle-left"></i>',rightArrow:'<i class="la la-angle-right"></i>'},startDate:a,endDate:new Date,clearBtn:!0}).on("changeDate",(function(e){console.log("End date selected:",e.format())})).on("clearDate",(function(e){console.log("End date cleared")})),console.log("End date picker enabled with startDate:",a)})).on("clearDate",(function(e){console.log("Start date cleared"),a()})),e(),$("#date_to").prop("disabled",!0)}(),setTimeout((function(){!function(){console.log("Initializing DataTable..."),$.fn.DataTable.isDataTable("#opnames-table")&&$("#opnames-table").DataTable().destroy();var e=$("#opnames-table").DataTable({processing:!0,serverSide:!0,destroy:!0,ajax:{url:$("#opnames-table").data("url"),type:"GET",data:function(e){return e.dealer_filter=$("#dealer_filter").val(),e.date_from=$("#date_from").val(),e.date_to=$("#date_to").val(),console.log("AJAX data being sent:",{dealer_filter:e.dealer_filter,date_from:e.date_from,date_to:e.date_to}),e},error:function(e,a,t){console.error("DataTables AJAX error:",a,t),console.error("Response:",e.responseText)}},columnDefs:[{targets:0,width:"15%"},{targets:1,width:"12%"},{targets:2,width:"15%"},{targets:3,width:"12%"},{targets:4,width:"10%"},{targets:5,width:"15%",className:"text-center"},{targets:6,width:"15%",className:"text-center"}],columns:[{data:"created_at",name:"created_at",orderable:!0},{data:"opname_date",name:"opname_date",orderable:!0},{data:"dealer_name",name:"dealer.name",orderable:!0},{data:"user_name",name:"user.name",orderable:!0},{data:"status",name:"status",orderable:!0},{data:"stock_info",name:"stock_info",orderable:!1,searchable:!1},{data:"action",name:"action",orderable:!1,searchable:!1}],order:[[0,"desc"]],pageLength:10,responsive:!0,ordering:!0,orderMulti:!1});(function(e){$("#kt_search").on("click",(function(){console.log("Filter button clicked");var a=$("#dealer_filter").val(),t=$("#date_from").val(),o=$("#date_to").val();console.log("Filtering with:",{dealer:a,dateFrom:t,dateTo:o}),e.ajax.reload()})),$("#kt_reset").on("click",(function(){console.log("Reset button clicked"),$("#dealer_filter").val(null).trigger("change.select2"),$("#date_from").datepicker("clearDates"),$("#date_to").datepicker("clearDates"),a(),e.ajax.reload()})),$("#date_from, #date_to").on("keypress",(function(e){13===e.which&&$("#kt_search").click()})),$("#dealer_filter").on("change",(function(){console.log("Dealer filter changed:",$(this).val())}))})(e),function(e){e.on("order.dt",(function(){console.log("Order changed:",e.order())})),e.on("processing.dt",(function(e,a,t){t?console.log("DataTable processing started"):console.log("DataTable processing finished")}))}(e)}()}),100)):console.error("DataTables not available!")}))})();
|
||||||
//# sourceMappingURL=index.js.map
|
//# sourceMappingURL=index.js.map
|
||||||
File diff suppressed because one or more lines are too long
@@ -196,8 +196,13 @@ function initializeDataTable() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
columnDefs: [
|
columnDefs: [
|
||||||
{ targets: 0, width: "15%" }, // Opname Date column
|
{ targets: 0, width: "15%" }, // Created At column
|
||||||
{ targets: 5, width: "15%", className: "text-center" }, // Action column
|
{ targets: 1, width: "12%" }, // Opname Date column
|
||||||
|
{ targets: 2, width: "15%" }, // Dealer column
|
||||||
|
{ targets: 3, width: "12%" }, // User column
|
||||||
|
{ targets: 4, width: "10%" }, // Status column
|
||||||
|
{ targets: 5, width: "15%", className: "text-center" }, // Stock Info column
|
||||||
|
{ targets: 6, width: "15%", className: "text-center" }, // Action column
|
||||||
],
|
],
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -225,6 +230,12 @@ function initializeDataTable() {
|
|||||||
name: "status",
|
name: "status",
|
||||||
orderable: true,
|
orderable: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
data: "stock_info",
|
||||||
|
name: "stock_info",
|
||||||
|
orderable: false,
|
||||||
|
searchable: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
data: "action",
|
data: "action",
|
||||||
name: "action",
|
name: "action",
|
||||||
@@ -232,7 +243,7 @@ function initializeDataTable() {
|
|||||||
searchable: false,
|
searchable: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
order: [[4, "desc"]], // Order by created_at desc
|
order: [[0, "desc"]], // Order by created_at desc
|
||||||
pageLength: 10,
|
pageLength: 10,
|
||||||
responsive: true,
|
responsive: true,
|
||||||
ordering: true,
|
ordering: true,
|
||||||
|
|||||||
@@ -739,22 +739,12 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tanggal Opname <small class="text-muted">(Default: Hari ini)</small></label>
|
<label>Tanggal Opname <small class="text-muted">(Default: Hari ini)</small></label>
|
||||||
<input type="text" name="opname_date" id="date-opname" class="form-control @error('opname_date') is-invalid @enderror" value="{{ old('opname_date', date('Y-m-d')) }}" placeholder="YYYY-MM-DD">
|
<input type="text" name="opname_date" id="date-opname" class="form-control" value="{{ old('opname_date', date('Y-m-d')) }}" placeholder="YYYY-MM-DD">
|
||||||
@error('opname_date')
|
|
||||||
<div class="invalid-feedback">
|
|
||||||
{{ $message }}
|
|
||||||
</div>
|
|
||||||
@enderror
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Keterangan</label>
|
<label>Keterangan <small class="text-muted">(Opsional)</small></label>
|
||||||
<textarea name="description" class="form-control @error('description') is-invalid @enderror" rows="3" placeholder="Keterangan opname">{{ old('description') }}</textarea>
|
<textarea name="description" class="form-control" rows="3" placeholder="Keterangan opname (opsional)">{{ old('description') }}</textarea>
|
||||||
@error('description')
|
|
||||||
<div class="invalid-feedback">
|
|
||||||
{{ $message }}
|
|
||||||
</div>
|
|
||||||
@enderror
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- List Produk dengan Stock -->
|
<!-- List Produk dengan Stock -->
|
||||||
@@ -764,9 +754,7 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
<tr>
|
<tr>
|
||||||
<th class="text-center">Produk</th>
|
<th class="text-center">Produk</th>
|
||||||
<th class="text-center">Dealer</th>
|
<th class="text-center">Dealer</th>
|
||||||
<th class="text-center">Stock Sistem</th>
|
|
||||||
<th class="text-center">Stock Fisik</th>
|
<th class="text-center">Stock Fisik</th>
|
||||||
<th class="text-center">Selisih</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -779,22 +767,11 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">{{ $product->name }}</td>
|
<td class="text-center">{{ $product->name }}</td>
|
||||||
<td class="text-center">{{ $mechanic->dealer_name }}</td>
|
<td class="text-center">{{ $mechanic->dealer_name }}</td>
|
||||||
<td class="text-center">
|
<td>
|
||||||
<span class="system-stock">{{ number_format($currentStock, 2) }}</span>
|
|
||||||
<input type="hidden" name="product_id[]" value="{{ $product->id }}">
|
<input type="hidden" name="product_id[]" value="{{ $product->id }}">
|
||||||
<input type="hidden" name="dealer_id_stock[]" value="{{ $mechanic->dealer_id }}">
|
<input type="hidden" name="dealer_id_stock[]" value="{{ $mechanic->dealer_id }}">
|
||||||
<input type="hidden" name="system_stock[]" value="{{ $currentStock }}">
|
<input type="hidden" name="system_stock[]" value="{{ $currentStock }}">
|
||||||
</td>
|
<input type="number" class="form-control physical-stock" name="physical_stock[]" step="0.01" placeholder="0.00" data-system="{{ $currentStock }}" value="{{ old('physical_stock.'.$loop->index, '0.00') }}" min="0">
|
||||||
<td>
|
|
||||||
<input type="number" class="form-control physical-stock @error('physical_stock.'.$loop->index) is-invalid @enderror" name="physical_stock[]" step="0.01" placeholder="0.00" data-system="{{ $currentStock }}" value="{{ old('physical_stock.'.$loop->index) }}">
|
|
||||||
@error('physical_stock.'.$loop->index)
|
|
||||||
<div class="invalid-feedback">
|
|
||||||
{{ $message }}
|
|
||||||
</div>
|
|
||||||
@enderror
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<span class="difference text-bold">0.00</span>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -806,6 +783,10 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
<button id="btn-save-opname" class="btn btn-warning btn-lg d-block w-100 mt-2">
|
<button id="btn-save-opname" class="btn btn-warning btn-lg d-block w-100 mt-2">
|
||||||
Simpan Opname
|
Simpan Opname
|
||||||
</button>
|
</button>
|
||||||
|
<small class="text-muted d-block text-center mt-2">
|
||||||
|
<i class="fa fa-info-circle"></i>
|
||||||
|
Stock fisik yang kosong akan otomatis diisi dengan 0
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -1353,49 +1334,39 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
// Save current tab to localStorage
|
// Save current tab to localStorage
|
||||||
localStorage.setItem('activeTab', '#opname');
|
localStorage.setItem('activeTab', '#opname');
|
||||||
|
|
||||||
// Validate form
|
// Set default values for empty fields and validate
|
||||||
var hasValidStock = false;
|
var hasValidStock = false;
|
||||||
var invalidRows = [];
|
|
||||||
|
|
||||||
$('.physical-stock').each(function(index) {
|
$('.physical-stock').each(function(index) {
|
||||||
var value = $(this).val();
|
var value = $(this).val();
|
||||||
var row = $(this).closest('tr');
|
|
||||||
var productName = row.find('td:first').text().trim();
|
|
||||||
|
|
||||||
// Check if value is valid (including 0)
|
// Set default value to 0 if empty
|
||||||
if (value !== '' && value !== null && value !== undefined) {
|
if (value === '' || value === null || value === undefined) {
|
||||||
var numValue = parseFloat(value);
|
$(this).val('0.00');
|
||||||
if (!isNaN(numValue) && numValue >= 0) {
|
value = '0.00';
|
||||||
hasValidStock = true;
|
}
|
||||||
// Ensure the value is properly formatted
|
|
||||||
$(this).val(numValue.toFixed(2));
|
// Validate and format the value
|
||||||
} else {
|
var numValue = parseFloat(value);
|
||||||
invalidRows.push(productName + ' (nilai tidak valid)');
|
if (!isNaN(numValue) && numValue >= 0) {
|
||||||
}
|
hasValidStock = true;
|
||||||
|
// Ensure the value is properly formatted
|
||||||
|
$(this).val(numValue.toFixed(2));
|
||||||
|
} else {
|
||||||
|
// If invalid, set to 0
|
||||||
|
$(this).val('0.00');
|
||||||
|
hasValidStock = true; // Still consider valid since we set default
|
||||||
}
|
}
|
||||||
// Don't remove elements here - let them stay for re-editing
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show error if no valid stock entries
|
// Always allow submission since we set defaults
|
||||||
if (!hasValidStock) {
|
if (!hasValidStock) {
|
||||||
|
// This should never happen now, but just in case
|
||||||
resetSubmitButton();
|
resetSubmitButton();
|
||||||
highlightInvalidFields();
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
title: 'Peringatan',
|
title: 'Peringatan',
|
||||||
text: 'Minimal harus ada satu produk dengan stock fisik yang diisi dengan benar!'
|
text: 'Terjadi kesalahan dalam validasi data. Silakan coba lagi.'
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error if there are invalid entries
|
|
||||||
if (invalidRows.length > 0) {
|
|
||||||
resetSubmitButton();
|
|
||||||
highlightInvalidFields();
|
|
||||||
Swal.fire({
|
|
||||||
icon: 'warning',
|
|
||||||
title: 'Data Tidak Valid',
|
|
||||||
text: 'Perbaiki data berikut: ' + invalidRows.join(', ')
|
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1457,21 +1428,6 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
|
|
||||||
// Date format is already YYYY-MM-DD, no conversion needed
|
// Date format is already YYYY-MM-DD, no conversion needed
|
||||||
|
|
||||||
// Clean up empty rows before submit (remove hidden inputs for truly empty fields only)
|
|
||||||
$('.physical-stock').each(function() {
|
|
||||||
var value = $(this).val();
|
|
||||||
var row = $(this).closest('tr');
|
|
||||||
|
|
||||||
// Only remove hidden inputs if physical stock is truly empty (not 0)
|
|
||||||
// Keep 0 values as they are valid input
|
|
||||||
if (value === '' || value === null || value === undefined) {
|
|
||||||
row.find('input[name="product_id[]"]').remove();
|
|
||||||
row.find('input[name="dealer_id_stock[]"]').remove();
|
|
||||||
row.find('input[name="system_stock[]"]').remove();
|
|
||||||
// But keep the physical_stock input so it can be re-edited if form fails
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Submit form
|
// Submit form
|
||||||
this.submit();
|
this.submit();
|
||||||
})
|
})
|
||||||
@@ -1690,31 +1646,34 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
|
|
||||||
// Function to update product counter
|
// Function to update product counter
|
||||||
function updateProductCounter() {
|
function updateProductCounter() {
|
||||||
var filledProducts = 0;
|
|
||||||
var totalProducts = $('.physical-stock').length;
|
var totalProducts = $('.physical-stock').length;
|
||||||
|
|
||||||
$('.physical-stock').each(function() {
|
// Update button text to show total products
|
||||||
var value = $(this).val();
|
|
||||||
// Count as filled if it's a valid number (including 0)
|
|
||||||
if (value !== '' && value !== null && value !== undefined && !isNaN(parseFloat(value)) && parseFloat(value) >= 0) {
|
|
||||||
filledProducts++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update button text to show progress
|
|
||||||
var buttonText = 'Simpan Opname';
|
var buttonText = 'Simpan Opname';
|
||||||
if (filledProducts > 0) {
|
buttonText += ` (${totalProducts} produk)`;
|
||||||
buttonText += ` (${filledProducts}/${totalProducts} produk)`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$('#btn-save-opname').hasClass('disabled')) {
|
if (!$('#btn-save-opname').hasClass('disabled')) {
|
||||||
$('#btn-save-opname').html(buttonText);
|
$('#btn-save-opname').html(buttonText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle when input loses focus - don't auto-fill, let user decide
|
// Initialize default values for physical stock inputs
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('.physical-stock').each(function() {
|
||||||
|
var value = $(this).val();
|
||||||
|
if (value === '' || value === null || value === undefined) {
|
||||||
|
$(this).val('0.00');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle when input loses focus - set default if empty
|
||||||
$(document).on('blur', '.physical-stock', function() {
|
$(document).on('blur', '.physical-stock', function() {
|
||||||
// Trigger calculation even for empty values
|
var value = $(this).val();
|
||||||
|
if (value === '' || value === null || value === undefined) {
|
||||||
|
$(this).val('0.00');
|
||||||
|
}
|
||||||
|
// Trigger calculation
|
||||||
$(this).trigger('input');
|
$(this).trigger('input');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1725,28 +1684,12 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
updateProductCounter(); // Update with current counter
|
updateProductCounter(); // Update with current counter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to show field error highlighting
|
// Set default values when user leaves field empty
|
||||||
function highlightInvalidFields() {
|
$(document).on('blur', '.physical-stock', function() {
|
||||||
$('.physical-stock').each(function() {
|
var value = $(this).val();
|
||||||
var value = $(this).val();
|
if (value === '' || value === null || value === undefined) {
|
||||||
var $input = $(this);
|
$(this).val('0.00');
|
||||||
|
}
|
||||||
if (value !== '' && value !== null && value !== undefined) {
|
|
||||||
var numValue = parseFloat(value);
|
|
||||||
if (isNaN(numValue) || numValue < 0) {
|
|
||||||
$input.addClass('is-invalid');
|
|
||||||
} else {
|
|
||||||
$input.removeClass('is-invalid');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$input.removeClass('is-invalid');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove error styling when user starts typing
|
|
||||||
$(document).on('input', '.physical-stock', function() {
|
|
||||||
$(this).removeClass('is-invalid');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove invalid styling when user starts typing in required fields
|
// Remove invalid styling when user starts typing in required fields
|
||||||
@@ -1779,6 +1722,9 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
$('#date-opname').val(today);
|
$('#date-opname').val(today);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize opname form
|
||||||
|
updateProductCounter();
|
||||||
|
|
||||||
// Initialize mutasi form
|
// Initialize mutasi form
|
||||||
updateRemoveButtonsMutasi();
|
updateRemoveButtonsMutasi();
|
||||||
|
|
||||||
@@ -1935,7 +1881,10 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
} else if (successMessage.toLowerCase().includes('opname') || activeTab === 'opname') {
|
} else if (successMessage.toLowerCase().includes('opname') || activeTab === 'opname') {
|
||||||
// Reset opname form after success
|
// Reset opname form after success
|
||||||
$('#opnameForm')[0].reset();
|
$('#opnameForm')[0].reset();
|
||||||
$('.physical-stock').val('').removeClass('is-invalid');
|
// Set default values for physical stock inputs
|
||||||
|
$('.physical-stock').each(function() {
|
||||||
|
$(this).val('0.00');
|
||||||
|
});
|
||||||
$('.difference').text('0.00').removeClass('text-success text-danger');
|
$('.difference').text('0.00').removeClass('text-success text-danger');
|
||||||
$("#btn-save-opname").attr("disabled", false);
|
$("#btn-save-opname").attr("disabled", false);
|
||||||
$("#btn-save-opname").removeClass("disabled");
|
$("#btn-save-opname").removeClass("disabled");
|
||||||
@@ -1963,6 +1912,12 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
$("#btn-save-opname").attr("disabled", false);
|
$("#btn-save-opname").attr("disabled", false);
|
||||||
$("#btn-save-opname").removeClass("disabled");
|
$("#btn-save-opname").removeClass("disabled");
|
||||||
$("#btn-save-opname").html('Simpan Opname');
|
$("#btn-save-opname").html('Simpan Opname');
|
||||||
|
// Set default values for physical stock inputs
|
||||||
|
$('.physical-stock').each(function() {
|
||||||
|
if ($(this).val() === '' || $(this).val() === null || $(this).val() === undefined) {
|
||||||
|
$(this).val('0.00');
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (activeTab === 'mutasi') {
|
} else if (activeTab === 'mutasi') {
|
||||||
// Reset button states for mutasi form
|
// Reset button states for mutasi form
|
||||||
$("#btn-save-mutasi").attr("disabled", false);
|
$("#btn-save-mutasi").attr("disabled", false);
|
||||||
|
|||||||
@@ -327,7 +327,7 @@
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="company-info">
|
<div class="company-info">
|
||||||
<div class="company-name">PT. CINTA KASIH BERSAMA</div>
|
<div class="company-name">PT. CIPTA KREASI BARU</div>
|
||||||
<div class="company-tagline">Warehouse Management System</div>
|
<div class="company-tagline">Warehouse Management System</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="document-title">Dokumen Mutasi Stock</div>
|
<div class="document-title">Dokumen Mutasi Stock</div>
|
||||||
@@ -528,7 +528,7 @@
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="print-info">
|
<div class="print-info">
|
||||||
Dicetak pada: {{ now()->format('d F Y H:i:s') }} |
|
Dicetak pada: {{ now()->format('d F Y H:i:s') }} |
|
||||||
Sistem Manajemen Gudang PT. Cinta Kasih Bersama
|
Sistem Manajemen Gudang PT. Cipta Kreasi Baru
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
<th>Dealer</th>
|
<th>Dealer</th>
|
||||||
<th>Pengguna</th>
|
<th>Pengguna</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
|
<th>Informasi Stock</th>
|
||||||
<th>Aksi</th>
|
<th>Aksi</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -114,6 +115,44 @@
|
|||||||
letter-spacing: normal;
|
letter-spacing: normal;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stock info column styling */
|
||||||
|
.stock-info-cell {
|
||||||
|
min-width: 120px;
|
||||||
|
max-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info-cell .text-success {
|
||||||
|
color: #28a745 !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info-cell .text-danger {
|
||||||
|
color: #dc3545 !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info-cell .text-muted {
|
||||||
|
color: #6c757d !important;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info-cell i {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments for stock info column */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.stock-info-cell {
|
||||||
|
min-width: 100px;
|
||||||
|
max-width: 120px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info-cell .text-muted {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
|||||||
@@ -287,7 +287,7 @@
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="company-info">
|
<div class="company-info">
|
||||||
<div class="company-name">PT. CINTA KASIH BERSAMA</div>
|
<div class="company-name">PT. CIPTA KREASI BARU</div>
|
||||||
<div class="company-tagline">Warehouse Management System</div>
|
<div class="company-tagline">Warehouse Management System</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="document-title">Laporan Stock Opname</div>
|
<div class="document-title">Laporan Stock Opname</div>
|
||||||
@@ -420,7 +420,7 @@
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="print-info">
|
<div class="print-info">
|
||||||
Dicetak pada: {{ now()->format('d F Y H:i:s') }} |
|
Dicetak pada: {{ now()->format('d F Y H:i:s') }} |
|
||||||
Sistem Manajemen Gudang PT. Cinta Kasih Bersama
|
Sistem Manajemen Gudang PT. Cipta Kreasi Baru
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user