fix opname default value, show different opname and hide system stock opname

This commit is contained in:
2025-07-03 13:55:49 +07:00
parent 9b3889ef1f
commit 0ef03fe7cb
8 changed files with 179 additions and 147 deletions

View File

@@ -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) {

View File

@@ -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

View File

@@ -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,

View File

@@ -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) {
$(this).val('0.00');
value = '0.00';
}
// Validate and format the value
var numValue = parseFloat(value); var numValue = parseFloat(value);
if (!isNaN(numValue) && numValue >= 0) { if (!isNaN(numValue) && numValue >= 0) {
hasValidStock = true; hasValidStock = true;
// Ensure the value is properly formatted // Ensure the value is properly formatted
$(this).val(numValue.toFixed(2)); $(this).val(numValue.toFixed(2));
} else { } else {
invalidRows.push(productName + ' (nilai tidak valid)'); // 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();
var $input = $(this); if (value === '' || value === null || value === undefined) {
$(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);

View File

@@ -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>

View File

@@ -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

View File

@@ -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>