fix edit products using new workflow mutations
This commit is contained in:
@@ -116,6 +116,13 @@ class MutationsController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
|
||||||
|
// Check if request came from transaction page
|
||||||
|
if ($request->has('from_transaction_page') || str_contains($request->header('referer', ''), '/transaction')) {
|
||||||
|
return redirect()->back()
|
||||||
|
->with('success', 'Mutasi berhasil dibuat dan terkirim ke dealer tujuan');
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->route('mutations.index')
|
return redirect()->route('mutations.index')
|
||||||
->with('success', 'Mutasi berhasil dibuat dan terkirim ke dealer tujuan');
|
->with('success', 'Mutasi berhasil dibuat dan terkirim ke dealer tujuan');
|
||||||
|
|
||||||
|
|||||||
@@ -41,4 +41,11 @@ class Dealer extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Stock::class);
|
return $this->hasMany(Stock::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function products()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Product::class, 'stocks', 'dealer_id', 'product_id')
|
||||||
|
->withPivot('quantity')
|
||||||
|
->withTimestamps();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ class Product extends Model
|
|||||||
return $this->hasMany(MutationDetail::class);
|
return $this->hasMany(MutationDetail::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function dealers()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Dealer::class, 'stocks', 'product_id', 'dealer_id')
|
||||||
|
->withPivot('quantity')
|
||||||
|
->withTimestamps();
|
||||||
|
}
|
||||||
|
|
||||||
// Helper method untuk mendapatkan total stock saat ini
|
// Helper method untuk mendapatkan total stock saat ini
|
||||||
public function getCurrentTotalStockAttribute()
|
public function getCurrentTotalStockAttribute()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -97,6 +97,17 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
border-color: #dc3545;
|
border-color: #dc3545;
|
||||||
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
|
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
|
||||||
}
|
}
|
||||||
|
.quantity-input-mutasi.is-invalid {
|
||||||
|
border-color: #dc3545;
|
||||||
|
background-color: #fff5f5;
|
||||||
|
}
|
||||||
|
.quantity-input-mutasi.is-invalid:focus {
|
||||||
|
border-color: #dc3545;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
|
||||||
|
}
|
||||||
|
.available-stock-mutasi {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@@ -156,35 +167,7 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (session('success'))
|
|
||||||
<div class="row mt-2 mb-2">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="alert alert—check alert-success fade show" role="alert">
|
|
||||||
<div class="alert-text">{{ session('success') }}</div>
|
|
||||||
<div class="alert-close">
|
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
|
||||||
<span aria-hidden="true"><i class="la la-close"></i></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if (session('error'))
|
|
||||||
<div class="row mt-2 mb-2">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="alert alert-danger fade show" role="alert">
|
|
||||||
<div class="alert-text">{{ session('error') }}</div>
|
|
||||||
<div class="alert-close">
|
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
|
||||||
<span aria-hidden="true"><i class="la la-close"></i></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<!--begin::Portlet-->
|
<!--begin::Portlet-->
|
||||||
@@ -581,7 +564,7 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 mb-3">
|
<div class="mt-4 mb-3">
|
||||||
<button class="btn btn-warning btn-lg button-save 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>
|
||||||
</div>
|
</div>
|
||||||
@@ -590,73 +573,84 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
|
|
||||||
<!-- Form Mutasi -->
|
<!-- Form Mutasi -->
|
||||||
<div class="tab-pane" id="mutasi" role="tabpanel">
|
<div class="tab-pane" id="mutasi" role="tabpanel">
|
||||||
<form action="#" method="POST" id="mutasiForm">
|
<form action="{{ route('mutations.store') }}" method="POST" id="mutasiForm">
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="form" value="mutasi">
|
<input type="hidden" name="from_dealer_id" value="{{ $mechanic->dealer_id }}">
|
||||||
<input type="hidden" name="mechanic_id" value="{{ $mechanic->id }}">
|
<input type="hidden" name="from_transaction_page" value="1">
|
||||||
<input type="hidden" name="dealer_id" value="{{ $mechanic->dealer_id }}">
|
|
||||||
|
|
||||||
<div class="form-group row">
|
<div class="form-group">
|
||||||
<div class="col-6">
|
<label>Dealer Asal</label>
|
||||||
<label>Tanggal Mutasi</label>
|
<input type="text" class="form-control" value="{{ $mechanic->dealer_name }}" disabled>
|
||||||
<input type="text" name="mutasi_date" id="date-mutasi" class="form-control" placeholder="Tanggal Mutasi" required>
|
<small class="text-muted">Otomatis terdeteksi dari akun Anda</small>
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<label>Jenis Mutasi</label>
|
|
||||||
<select name="mutasi_type" class="form-control" required>
|
|
||||||
<option value="">Pilih Jenis</option>
|
|
||||||
<option value="masuk">Barang Masuk</option>
|
|
||||||
<option value="keluar">Barang Keluar</option>
|
|
||||||
<option value="transfer">Transfer</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Keterangan</label>
|
<label for="to_dealer_id">Dealer Tujuan <span class="text-danger">*</span></label>
|
||||||
<textarea name="description" class="form-control" rows="3" placeholder="Keterangan mutasi"></textarea>
|
<select name="to_dealer_id" id="to_dealer_id" class="form-control" required>
|
||||||
|
<option value="">Pilih Dealer Tujuan</option>
|
||||||
|
@php
|
||||||
|
$dealers = \App\Models\Dealer::where('id', '!=', $mechanic->dealer_id)->get();
|
||||||
|
@endphp
|
||||||
|
@foreach($dealers as $dealer)
|
||||||
|
<option value="{{ $dealer->id }}">{{ $dealer->name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<label class="form-label mb-0">Detail Produk <span class="text-danger">*</span></label>
|
||||||
|
<button type="button" class="btn btn-success btn-sm" id="add-product-mutasi">
|
||||||
|
<i class="fa fa-plus"></i> Tambah Produk
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- List Produk untuk Mutasi -->
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered table-sm" id="products-table-mutasi">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="5%">
|
<th width="40%">Produk</th>
|
||||||
<input type="checkbox" id="select-all-mutasi">
|
<th width="20%">Stock Tersedia</th>
|
||||||
</th>
|
<th width="25%">Quantity</th>
|
||||||
<th>Produk</th>
|
<th width="15%">Aksi</th>
|
||||||
<th>Stock Saat Ini</th>
|
|
||||||
<th>Jumlah Mutasi</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody id="products-tbody-mutasi">
|
||||||
{{-- Dealer/Mechanic - Show products for current dealer only --}}
|
<tr class="product-row-mutasi" data-index="0">
|
||||||
|
<td>
|
||||||
|
<select name="products[0][product_id]" class="form-control product-select-mutasi" required>
|
||||||
|
<option value="">Pilih Produk</option>
|
||||||
@foreach($products as $product)
|
@foreach($products as $product)
|
||||||
@php
|
<option value="{{ $product->id }}">{{ $product->name }}</option>
|
||||||
$stock = $product->stocks->first();
|
@endforeach
|
||||||
$currentStock = $stock ? $stock->quantity : 0;
|
</select>
|
||||||
@endphp
|
</td>
|
||||||
<tr>
|
<td class="text-center">
|
||||||
<td>
|
<span class="available-stock-mutasi text-muted">-</span>
|
||||||
<input type="checkbox" class="mutasi-product-checkbox" name="selected_products[]" value="{{ $product->id }}_{{ $mechanic->dealer_id }}">
|
|
||||||
</td>
|
</td>
|
||||||
<td>{{ $product->name }}</td>
|
|
||||||
<td class="text-right">{{ number_format($currentStock, 2) }}</td>
|
|
||||||
<td>
|
<td>
|
||||||
<input type="number" class="form-control mutasi-quantity" name="mutasi_quantity[{{ $product->id }}_{{ $mechanic->dealer_id }}]" step="0.01" placeholder="0.00" disabled>
|
<input type="number"
|
||||||
<input type="hidden" name="product_id_mutasi[]" value="{{ $product->id }}">
|
name="products[0][quantity_requested]"
|
||||||
<input type="hidden" name="dealer_id_mutasi[]" value="{{ $mechanic->dealer_id }}">
|
class="form-control quantity-input-mutasi"
|
||||||
|
min="0.01"
|
||||||
|
step="0.01"
|
||||||
|
placeholder="0"
|
||||||
|
required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm remove-product-mutasi" disabled>
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 mb-3">
|
<div class="mt-4 mb-3">
|
||||||
<button class="btn btn-warning btn-lg button-save d-block w-100 mt-2">
|
<button id="btn-save-mutasi" type="submit" class="btn btn-warning btn-lg d-block w-100 mt-2">
|
||||||
Simpan Mutasi
|
Kirim Mutasi
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -881,9 +875,9 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$(".button-save").attr("disabled", true);
|
$("#btn-save-opname").attr("disabled", true);
|
||||||
$(".button-save").addClass("disabled");
|
$("#btn-save-opname").addClass("disabled");
|
||||||
$(".button-save").html('<i class="fa fa-spinner fa-spin"></i> Menyimpan...');
|
$("#btn-save-opname").html('<i class="fa fa-spinner fa-spin"></i> Menyimpan...');
|
||||||
|
|
||||||
// Date format is already YYYY-MM-DD, no conversion needed
|
// Date format is already YYYY-MM-DD, no conversion needed
|
||||||
|
|
||||||
@@ -906,10 +900,158 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
this.submit();
|
this.submit();
|
||||||
})
|
})
|
||||||
|
|
||||||
$("#mutasiForm").submit(function(e) {
|
// Handle mutasi form submission
|
||||||
$(".button-save").attr("disabled");
|
$(document).on('submit', '#mutasiForm', function(e) {
|
||||||
$(".button-save").addClass("disabled");
|
e.preventDefault();
|
||||||
return true;
|
|
||||||
|
// Validate form
|
||||||
|
var isValid = true;
|
||||||
|
var errorMessages = [];
|
||||||
|
|
||||||
|
// Check if dealer tujuan is selected
|
||||||
|
var dealerTujuan = $('#to_dealer_id').val();
|
||||||
|
console.log('Dealer tujuan value:', dealerTujuan);
|
||||||
|
if (!dealerTujuan || dealerTujuan === '') {
|
||||||
|
errorMessages.push('Pilih dealer tujuan');
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if at least one product is selected with quantity
|
||||||
|
var hasProducts = false;
|
||||||
|
var productCount = 0;
|
||||||
|
$('.product-select-mutasi').each(function(index) {
|
||||||
|
var productId = $(this).val();
|
||||||
|
var quantity = $(this).closest('tr').find('.quantity-input-mutasi').val();
|
||||||
|
|
||||||
|
console.log(`Product ${index}:`, 'ID =', productId, 'Qty =', quantity);
|
||||||
|
|
||||||
|
if (productId && productId !== '' && quantity && parseFloat(quantity) > 0) {
|
||||||
|
hasProducts = true;
|
||||||
|
productCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasProducts) {
|
||||||
|
errorMessages.push('Minimal pilih satu produk dengan quantity yang valid');
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional validation: check all required fields
|
||||||
|
var requiredFields = [
|
||||||
|
{name: 'to_dealer_id', label: 'Dealer Tujuan'},
|
||||||
|
{name: 'from_dealer_id', label: 'Dealer Asal'}
|
||||||
|
];
|
||||||
|
|
||||||
|
requiredFields.forEach(function(field) {
|
||||||
|
var value = $(`[name="${field.name}"]`).val();
|
||||||
|
console.log(`${field.name} value:`, value);
|
||||||
|
if (!value || value === '') {
|
||||||
|
errorMessages.push(`${field.label} harus diisi`);
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check products array
|
||||||
|
var hasValidProduct = false;
|
||||||
|
$('select[name^="products["][name$="][product_id]"]').each(function(index) {
|
||||||
|
var productId = $(this).val();
|
||||||
|
var quantityName = $(this).attr('name').replace('[product_id]', '[quantity_requested]');
|
||||||
|
var quantity = $(`[name="${quantityName}"]`).val();
|
||||||
|
|
||||||
|
console.log(`Product validation ${index}:`, productId, quantity);
|
||||||
|
|
||||||
|
if (productId && productId !== '' && quantity && parseFloat(quantity) > 0) {
|
||||||
|
hasValidProduct = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasValidProduct) {
|
||||||
|
errorMessages.push('Minimal harus ada satu produk dengan quantity yang valid');
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Final validation result:', isValid);
|
||||||
|
console.log('Error messages:', errorMessages);
|
||||||
|
|
||||||
|
// Check for invalid quantities
|
||||||
|
var hasInvalidQuantity = false;
|
||||||
|
$('.quantity-input-mutasi').each(function() {
|
||||||
|
if ($(this).hasClass('is-invalid')) {
|
||||||
|
hasInvalidQuantity = true;
|
||||||
|
return false; // break loop
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasInvalidQuantity) {
|
||||||
|
errorMessages.push('Perbaiki quantity yang melebihi stock tersedia');
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
// Highlight invalid fields
|
||||||
|
if (!$('#to_dealer_id').val()) {
|
||||||
|
$('#to_dealer_id').addClass('is-invalid');
|
||||||
|
} else {
|
||||||
|
$('#to_dealer_id').removeClass('is-invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check products
|
||||||
|
$('select[name^="products["][name$="][product_id]"]').each(function() {
|
||||||
|
var productId = $(this).val();
|
||||||
|
var quantityInput = $(this).closest('tr').find('.quantity-input-mutasi');
|
||||||
|
var quantity = quantityInput.val();
|
||||||
|
|
||||||
|
if (!productId || productId === '') {
|
||||||
|
$(this).addClass('is-invalid');
|
||||||
|
} else {
|
||||||
|
$(this).removeClass('is-invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quantity || parseFloat(quantity) <= 0) {
|
||||||
|
quantityInput.addClass('is-invalid');
|
||||||
|
} else {
|
||||||
|
quantityInput.removeClass('is-invalid');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof Swal !== 'undefined') {
|
||||||
|
Swal.fire({
|
||||||
|
type: 'warning',
|
||||||
|
title: 'Validasi Gagal',
|
||||||
|
text: errorMessages.join(', ')
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
alert('Validasi Gagal: ' + errorMessages.join(', '));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show confirmation
|
||||||
|
if (typeof Swal !== 'undefined') {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Kirim Mutasi?',
|
||||||
|
text: "Mutasi akan dikirim ke dealer tujuan",
|
||||||
|
type: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#ffc107',
|
||||||
|
cancelButtonColor: '#6c757d',
|
||||||
|
confirmButtonText: 'Ya, Kirim',
|
||||||
|
cancelButtonText: 'Batal'
|
||||||
|
}).then((result) => {
|
||||||
|
console.log('SweetAlert result:', result);
|
||||||
|
if (result.value === true) { // For older SweetAlert2
|
||||||
|
console.log('Confirmed, submitting...');
|
||||||
|
submitMutasiFormDirect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Fallback to native confirm
|
||||||
|
if (confirm('Kirim Mutasi? Mutasi akan dikirim ke dealer tujuan')) {
|
||||||
|
submitMutasiFormDirect();
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
$("#date-work").datepicker({
|
$("#date-work").datepicker({
|
||||||
@@ -983,8 +1125,8 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
buttonText += ` (${filledProducts}/${totalProducts} produk)`;
|
buttonText += ` (${filledProducts}/${totalProducts} produk)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$('.button-save').hasClass('disabled')) {
|
if (!$('#btn-save-opname').hasClass('disabled')) {
|
||||||
$('.button-save').html(buttonText);
|
$('#btn-save-opname').html(buttonText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -996,8 +1138,8 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
|
|
||||||
// Function to reset button state if validation fails
|
// Function to reset button state if validation fails
|
||||||
function resetSubmitButton() {
|
function resetSubmitButton() {
|
||||||
$(".button-save").attr("disabled", false);
|
$("#btn-save-opname").attr("disabled", false);
|
||||||
$(".button-save").removeClass("disabled");
|
$("#btn-save-opname").removeClass("disabled");
|
||||||
updateProductCounter(); // Update with current counter
|
updateProductCounter(); // Update with current counter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1033,6 +1175,18 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
$('#date-opname').val(today);
|
$('#date-opname').val(today);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize mutasi form
|
||||||
|
updateRemoveButtonsMutasi();
|
||||||
|
|
||||||
|
// Check if we should show mutasi tab (after form submission)
|
||||||
|
@if(session('success') || session('error') || $errors->any())
|
||||||
|
// Activate stock tab and mutasi sub-tab
|
||||||
|
$('.nav-link[href="#stock"]').tab('show');
|
||||||
|
setTimeout(function() {
|
||||||
|
$('.nav-link[href="#mutasi"]').tab('show');
|
||||||
|
}, 100);
|
||||||
|
@endif
|
||||||
|
|
||||||
// Check if there are validation errors
|
// Check if there are validation errors
|
||||||
if ($('.is-invalid').length > 0) {
|
if ($('.is-invalid').length > 0) {
|
||||||
// Scroll to first error
|
// Scroll to first error
|
||||||
@@ -1043,7 +1197,7 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
// Show alert for validation errors
|
// Show alert for validation errors
|
||||||
@if(session('error'))
|
@if(session('error'))
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: 'error',
|
type: 'error',
|
||||||
title: 'Terjadi Kesalahan',
|
title: 'Terjadi Kesalahan',
|
||||||
text: '{{ session("error") }}',
|
text: '{{ session("error") }}',
|
||||||
confirmButtonText: 'OK'
|
confirmButtonText: 'OK'
|
||||||
@@ -1051,10 +1205,94 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
@endif
|
@endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-dismiss alerts after 5 seconds
|
|
||||||
setTimeout(function() {
|
|
||||||
$('.alert').fadeOut('slow');
|
// Handle success/error messages for both opname and mutasi
|
||||||
}, 5000);
|
@if(session('success'))
|
||||||
|
Swal.fire({
|
||||||
|
type: 'success',
|
||||||
|
title: 'Berhasil!',
|
||||||
|
text: '{{ session("success") }}',
|
||||||
|
timer: 3000,
|
||||||
|
showConfirmButton: false
|
||||||
|
}).then(() => {
|
||||||
|
// Check if this is from mutasi form (contains "Mutasi" in message)
|
||||||
|
var successMessage = '{{ session("success") }}';
|
||||||
|
if (successMessage.toLowerCase().includes('mutasi')) {
|
||||||
|
// Reset mutasi form after success
|
||||||
|
$('#mutasiForm')[0].reset();
|
||||||
|
$('#products-tbody-mutasi').html(`
|
||||||
|
<tr class="product-row-mutasi" data-index="0">
|
||||||
|
<td>
|
||||||
|
<select name="products[0][product_id]" class="form-control product-select-mutasi" required>
|
||||||
|
<option value="">Pilih Produk</option>
|
||||||
|
@foreach($products as $product)
|
||||||
|
<option value="{{ $product->id }}">{{ $product->name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="available-stock-mutasi text-muted">-</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="number"
|
||||||
|
name="products[0][quantity_requested]"
|
||||||
|
class="form-control quantity-input-mutasi"
|
||||||
|
min="0.01"
|
||||||
|
step="0.01"
|
||||||
|
placeholder="0"
|
||||||
|
required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm remove-product-mutasi" disabled>
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
productIndexMutasi = 0;
|
||||||
|
updateRemoveButtonsMutasi();
|
||||||
|
$("#btn-save-mutasi").attr("disabled", false);
|
||||||
|
$("#btn-save-mutasi").removeClass("disabled");
|
||||||
|
$("#btn-save-mutasi").html('Kirim Mutasi');
|
||||||
|
} else if (successMessage.toLowerCase().includes('opname')) {
|
||||||
|
// Reset opname form after success
|
||||||
|
$('#opnameForm')[0].reset();
|
||||||
|
$('.physical-stock').val('').removeClass('is-invalid');
|
||||||
|
$('.difference').text('0.00').removeClass('text-success text-danger');
|
||||||
|
$("#btn-save-opname").attr("disabled", false);
|
||||||
|
$("#btn-save-opname").removeClass("disabled");
|
||||||
|
$("#btn-save-opname").html('Simpan Opname');
|
||||||
|
// Set default date
|
||||||
|
var today = new Date().toISOString().split('T')[0];
|
||||||
|
$('#date-opname').val(today);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(session('error'))
|
||||||
|
Swal.fire({
|
||||||
|
type: 'error',
|
||||||
|
title: 'Terjadi Kesalahan',
|
||||||
|
text: '{{ session("error") }}',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
@endif
|
||||||
|
|
||||||
|
// Handle validation errors
|
||||||
|
@if($errors->any())
|
||||||
|
var errorMessages = [];
|
||||||
|
@foreach($errors->all() as $error)
|
||||||
|
errorMessages.push('{{ $error }}');
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
type: 'error',
|
||||||
|
title: 'Validasi Gagal',
|
||||||
|
html: errorMessages.join('<br>'),
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
@endif
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle opname date field - set default if becomes empty
|
// Handle opname date field - set default if becomes empty
|
||||||
@@ -1065,19 +1303,178 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle mutasi product selection
|
// Handle mutasi form - similar to create mutation
|
||||||
$(document).on('change', '.mutasi-product-checkbox', function() {
|
var productIndexMutasi = 0;
|
||||||
var quantityInput = $(this).closest('tr').find('.mutasi-quantity');
|
|
||||||
if ($(this).is(':checked')) {
|
|
||||||
quantityInput.prop('disabled', false);
|
|
||||||
|
// Function to submit mutasi form directly
|
||||||
|
function submitMutasiFormDirect() {
|
||||||
|
console.log('=== SUBMITTING FORM ===');
|
||||||
|
|
||||||
|
// Set loading state
|
||||||
|
$("#btn-save-mutasi").attr("disabled", true);
|
||||||
|
$("#btn-save-mutasi").addClass("disabled");
|
||||||
|
$("#btn-save-mutasi").html('<i class="fa fa-spinner fa-spin"></i> Mengirim...');
|
||||||
|
|
||||||
|
// Get form element directly
|
||||||
|
var form = document.getElementById('mutasiForm');
|
||||||
|
|
||||||
|
if (!form) {
|
||||||
|
console.error('Form not found!');
|
||||||
|
alert('Form tidak ditemukan!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Form found:', form);
|
||||||
|
console.log('Form action:', form.action);
|
||||||
|
console.log('Form method:', form.method);
|
||||||
|
|
||||||
|
// Check form data
|
||||||
|
var formData = new FormData(form);
|
||||||
|
console.log('Form data:');
|
||||||
|
for (var pair of formData.entries()) {
|
||||||
|
console.log(pair[0] + ': ' + pair[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit form directly - simplified approach
|
||||||
|
try {
|
||||||
|
console.log('Attempting form.submit()...');
|
||||||
|
|
||||||
|
// Temporarily remove the submit event listener to prevent recursion
|
||||||
|
$(form).off('submit');
|
||||||
|
|
||||||
|
// Submit the form
|
||||||
|
form.submit();
|
||||||
|
console.log('form.submit() called successfully');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Form submission error:', error);
|
||||||
|
// Reset button if submission fails
|
||||||
|
$("#btn-save-mutasi").attr("disabled", false);
|
||||||
|
$("#btn-save-mutasi").removeClass("disabled");
|
||||||
|
$("#btn-save-mutasi").html('Kirim Mutasi');
|
||||||
|
alert('Terjadi kesalahan saat mengirim form. Silakan coba lagi.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Add product row for mutasi
|
||||||
|
$('#add-product-mutasi').click(function() {
|
||||||
|
productIndexMutasi++;
|
||||||
|
var newRow = `
|
||||||
|
<tr class="product-row-mutasi" data-index="${productIndexMutasi}">
|
||||||
|
<td>
|
||||||
|
<select name="products[${productIndexMutasi}][product_id]" class="form-control product-select-mutasi" required>
|
||||||
|
<option value="">Pilih Produk</option>
|
||||||
|
@foreach($products as $product)
|
||||||
|
<option value="{{ $product->id }}">{{ $product->name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="available-stock-mutasi text-muted">-</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="number"
|
||||||
|
name="products[${productIndexMutasi}][quantity_requested]"
|
||||||
|
class="form-control quantity-input-mutasi"
|
||||||
|
min="0.01"
|
||||||
|
step="0.01"
|
||||||
|
placeholder="0"
|
||||||
|
required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm remove-product-mutasi">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
|
||||||
|
$('#products-tbody-mutasi').append(newRow);
|
||||||
|
updateRemoveButtonsMutasi();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove product row for mutasi
|
||||||
|
$(document).on('click', '.remove-product-mutasi', function() {
|
||||||
|
$(this).closest('tr').remove();
|
||||||
|
updateRemoveButtonsMutasi();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update remove buttons state for mutasi
|
||||||
|
function updateRemoveButtonsMutasi() {
|
||||||
|
var rows = $('.product-row-mutasi');
|
||||||
|
if (rows.length <= 1) {
|
||||||
|
$('.remove-product-mutasi').prop('disabled', true);
|
||||||
} else {
|
} else {
|
||||||
quantityInput.prop('disabled', true).val('');
|
$('.remove-product-mutasi').prop('disabled', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle product selection change for mutasi - get stock info
|
||||||
|
$(document).on('change', '.product-select-mutasi', function() {
|
||||||
|
var productId = $(this).val();
|
||||||
|
var dealerId = {{ $mechanic->dealer_id }};
|
||||||
|
var stockSpan = $(this).closest('tr').find('.available-stock-mutasi');
|
||||||
|
|
||||||
|
if (productId) {
|
||||||
|
// Get stock info via AJAX
|
||||||
|
$.ajax({
|
||||||
|
url: '{{ route("mutations.get-product-stock") }}',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
product_id: productId,
|
||||||
|
dealer_id: dealerId
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
stockSpan.text(parseFloat(response.current_stock).toFixed(2));
|
||||||
|
stockSpan.removeClass('text-muted').addClass('text-info');
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
stockSpan.text('-');
|
||||||
|
stockSpan.removeClass('text-info').addClass('text-muted');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
stockSpan.text('-');
|
||||||
|
stockSpan.removeClass('text-info').addClass('text-muted');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Select all mutasi products
|
// Validate quantity against available stock for mutasi
|
||||||
$('#select-all-mutasi').change(function() {
|
$(document).on('input', '.quantity-input-mutasi', function() {
|
||||||
$('.mutasi-product-checkbox').prop('checked', this.checked).trigger('change');
|
var quantity = parseFloat($(this).val()) || 0;
|
||||||
|
var stockText = $(this).closest('tr').find('.available-stock-mutasi').text();
|
||||||
|
var availableStock = parseFloat(stockText) || 0;
|
||||||
|
|
||||||
|
if (quantity > availableStock && availableStock > 0) {
|
||||||
|
$(this).addClass('is-invalid');
|
||||||
|
if (!$(this).siblings('.invalid-feedback').length) {
|
||||||
|
$(this).after('<div class="invalid-feedback">Quantity melebihi stock tersedia</div>');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$(this).removeClass('is-invalid');
|
||||||
|
$(this).siblings('.invalid-feedback').remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove error styling when user starts filling fields
|
||||||
|
$(document).on('change', '#to_dealer_id', function() {
|
||||||
|
$(this).removeClass('is-invalid');
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('change', '.product-select-mutasi', function() {
|
||||||
|
$(this).removeClass('is-invalid');
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('input', '.quantity-input-mutasi', function() {
|
||||||
|
if (parseFloat($(this).val()) > 0) {
|
||||||
|
$(this).removeClass('is-invalid');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="btn-group btn-group-sm" role="group">
|
<div class="btn-group btn-group-sm" role="group">
|
||||||
<!-- View Button -->
|
<!-- View Button -->
|
||||||
<a href="{{ route('mutations.show', $row->id) }}"
|
<a href="{{ route('mutations.show', $row->id) }}"
|
||||||
class="btn btn-sm btn-outline-info me-1">
|
class="btn btn-sm btn-info mr-2">
|
||||||
Detail
|
Detail
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<!-- Receive Button (untuk dealer tujuan) -->
|
<!-- Receive Button (untuk dealer tujuan) -->
|
||||||
@if(auth()->user()->dealer_id == $row->to_dealer_id)
|
@if(auth()->user()->dealer_id == $row->to_dealer_id)
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-outline-primary btn-receive me-1"
|
class="btn btn-sm btn-primary btn-receive mr-2"
|
||||||
data-id="{{ $row->id }}"
|
data-id="{{ $row->id }}"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
data-bs-target="#receiveModal{{ $row->id }}">
|
data-bs-target="#receiveModal{{ $row->id }}">
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
<!-- Cancel Button (untuk pengirim) -->
|
<!-- Cancel Button (untuk pengirim) -->
|
||||||
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-outline-warning btn-cancel me-1"
|
class="btn btn-sm btn-warning btn-cancel mr-2"
|
||||||
data-id="{{ $row->id }}">
|
data-id="{{ $row->id }}">
|
||||||
Batal
|
Batal
|
||||||
</button>
|
</button>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<!-- Approve Button (untuk pengirim atau admin) -->
|
<!-- Approve Button (untuk pengirim atau admin) -->
|
||||||
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-outline-success btn-approve me-1"
|
class="btn btn-sm btn-success btn-approve mr-2"
|
||||||
data-id="{{ $row->id }}"
|
data-id="{{ $row->id }}"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
data-bs-target="#approveModal{{ $row->id }}">
|
data-bs-target="#approveModal{{ $row->id }}">
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<!-- Reject Button (untuk pengirim atau admin) -->
|
<!-- Reject Button (untuk pengirim atau admin) -->
|
||||||
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
@if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin'))
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-outline-danger btn-reject me-1"
|
class="btn btn-sm btn-danger btn-reject mr-2"
|
||||||
data-id="{{ $row->id }}"
|
data-id="{{ $row->id }}"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
data-bs-target="#rejectModal{{ $row->id }}">
|
data-bs-target="#rejectModal{{ $row->id }}">
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
<!-- Complete/Receive Button -->
|
<!-- Complete/Receive Button -->
|
||||||
@can('complete-mutation')
|
@can('complete-mutation')
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-outline-primary btn-complete me-1"
|
class="btn btn-sm btn-primary btn-complete mr-2"
|
||||||
data-id="{{ $row->id }}"
|
data-id="{{ $row->id }}"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
data-bs-target="#completeModal{{ $row->id }}">
|
data-bs-target="#completeModal{{ $row->id }}">
|
||||||
@@ -66,20 +66,12 @@
|
|||||||
<!-- Cancel Button -->
|
<!-- Cancel Button -->
|
||||||
@can('edit-mutation')
|
@can('edit-mutation')
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-outline-warning btn-cancel me-1"
|
class="btn btn-sm btn-warning btn-cancel mr-2"
|
||||||
data-id="{{ $row->id }}">
|
data-id="{{ $row->id }}">
|
||||||
Batal
|
Batal
|
||||||
</button>
|
</button>
|
||||||
@endcan
|
@endcan
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if(in_array($row->status->value, ['pending', 'approved']) && auth()->user()->id === $row->requested_by)
|
|
||||||
<!-- Edit Button (only for creator and if still pending/approved) -->
|
|
||||||
<a href="{{ route('mutations.edit', $row->id) }}"
|
|
||||||
class="btn btn-sm btn-outline-secondary me-1">
|
|
||||||
Edit
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal untuk Approve -->
|
<!-- Modal untuk Approve -->
|
||||||
@@ -88,7 +80,6 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="approveModalLabel{{ $row->id }}">Setujui Mutasi</h5>
|
<h5 class="modal-title" id="approveModalLabel{{ $row->id }}">Setujui Mutasi</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ route('mutations.approve', $row->id) }}" method="POST" class="approve-form">
|
<form action="{{ route('mutations.approve', $row->id) }}" method="POST" class="approve-form">
|
||||||
@csrf
|
@csrf
|
||||||
@@ -154,7 +145,6 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="rejectModalLabel{{ $row->id }}">Tolak Mutasi</h5>
|
<h5 class="modal-title" id="rejectModalLabel{{ $row->id }}">Tolak Mutasi</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ route('mutations.reject', $row->id) }}" method="POST">
|
<form action="{{ route('mutations.reject', $row->id) }}" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
@@ -182,7 +172,6 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="receiveModalLabel{{ $row->id }}">Terima Mutasi</h5>
|
<h5 class="modal-title" id="receiveModalLabel{{ $row->id }}">Terima Mutasi</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ route('mutations.receive', $row->id) }}" method="POST">
|
<form action="{{ route('mutations.receive', $row->id) }}" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
@@ -251,7 +240,6 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="completeModalLabel{{ $row->id }}">Selesaikan Mutasi</h5>
|
<h5 class="modal-title" id="completeModalLabel{{ $row->id }}">Selesaikan Mutasi</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ route('mutations.complete', $row->id) }}" method="POST">
|
<form action="{{ route('mutations.complete', $row->id) }}" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
Tabel Mutasi
|
Tabel Mutasi
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
@can('create', $menus['mutations.index'])
|
||||||
<div class="kt-portlet__head-toolbar">
|
<div class="kt-portlet__head-toolbar">
|
||||||
<div class="kt-portlet__head-wrapper">
|
<div class="kt-portlet__head-wrapper">
|
||||||
<div class="kt-portlet__head-actions">
|
<div class="kt-portlet__head-actions">
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@endcan
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="kt-portlet__body">
|
<div class="kt-portlet__body">
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<div class="kt-portlet__head-toolbar">
|
<div class="kt-portlet__head-toolbar">
|
||||||
<div class="kt-portlet__head-wrapper">
|
<div class="kt-portlet__head-wrapper">
|
||||||
<div class="kt-portlet__head-actions">
|
<div class="kt-portlet__head-actions">
|
||||||
<button type="button" class="btn btn-bold btn-label-brand btn-sm" id="addProductCategory"> Tambah </button>
|
<button type="button" class="btn btn-bold btn-label-brand btn--sm" id="addProductCategory"> Tambah </button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user