diff --git a/app/Http/Controllers/WarehouseManagement/MutationsController.php b/app/Http/Controllers/WarehouseManagement/MutationsController.php index f5570ba..bbb57a9 100644 --- a/app/Http/Controllers/WarehouseManagement/MutationsController.php +++ b/app/Http/Controllers/WarehouseManagement/MutationsController.php @@ -116,6 +116,13 @@ class MutationsController extends Controller } 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') ->with('success', 'Mutasi berhasil dibuat dan terkirim ke dealer tujuan'); diff --git a/app/Models/Dealer.php b/app/Models/Dealer.php index 2869f13..792e7ca 100644 --- a/app/Models/Dealer.php +++ b/app/Models/Dealer.php @@ -41,4 +41,11 @@ class Dealer extends Model { return $this->hasMany(Stock::class); } + + public function products() + { + return $this->belongsToMany(Product::class, 'stocks', 'dealer_id', 'product_id') + ->withPivot('quantity') + ->withTimestamps(); + } } diff --git a/app/Models/Product.php b/app/Models/Product.php index 8eb7f30..de41a74 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -29,6 +29,13 @@ class Product extends Model 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 public function getCurrentTotalStockAttribute() { diff --git a/resources/views/transaction/index.blade.php b/resources/views/transaction/index.blade.php index 74b1d06..a46053b 100644 --- a/resources/views/transaction/index.blade.php +++ b/resources/views/transaction/index.blade.php @@ -97,6 +97,17 @@ use Illuminate\Support\Facades\Auth; border-color: #dc3545; 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; + } @endsection @@ -156,35 +167,7 @@ use Illuminate\Support\Facades\Auth; - @if (session('success')) -
-
- -
-
- @endif - @if (session('error')) -
-
- -
-
- @endif
@@ -581,7 +564,7 @@ use Illuminate\Support\Facades\Auth;
-
@@ -590,73 +573,84 @@ use Illuminate\Support\Facades\Auth;
-
+ @csrf - - - + + -
-
- - -
-
- - -
+
+ + + Otomatis terdeteksi dari akun Anda
- - + +
- - -
- - - - - - - - - - - {{-- Dealer/Mechanic - Show products for current dealer only --}} - @foreach($products as $product) - @php - $stock = $product->stocks->first(); - $currentStock = $stock ? $stock->quantity : 0; - @endphp + +
+
+ + +
+ +
+
- - ProdukStock Saat IniJumlah Mutasi
+ + + + + + + + + + - - + - @endforeach - -
ProdukStock TersediaQuantityAksi
- + + + - {{ $product->name }}{{ number_format($currentStock, 2) }} - - - + + +
+ + +
-
@@ -881,9 +875,9 @@ use Illuminate\Support\Facades\Auth; return false; } - $(".button-save").attr("disabled", true); - $(".button-save").addClass("disabled"); - $(".button-save").html(' Menyimpan...'); + $("#btn-save-opname").attr("disabled", true); + $("#btn-save-opname").addClass("disabled"); + $("#btn-save-opname").html(' Menyimpan...'); // Date format is already YYYY-MM-DD, no conversion needed @@ -906,10 +900,158 @@ use Illuminate\Support\Facades\Auth; this.submit(); }) - $("#mutasiForm").submit(function(e) { - $(".button-save").attr("disabled"); - $(".button-save").addClass("disabled"); - return true; + // Handle mutasi form submission + $(document).on('submit', '#mutasiForm', function(e) { + e.preventDefault(); + + // 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({ @@ -983,8 +1125,8 @@ use Illuminate\Support\Facades\Auth; buttonText += ` (${filledProducts}/${totalProducts} produk)`; } - if (!$('.button-save').hasClass('disabled')) { - $('.button-save').html(buttonText); + if (!$('#btn-save-opname').hasClass('disabled')) { + $('#btn-save-opname').html(buttonText); } } @@ -996,8 +1138,8 @@ use Illuminate\Support\Facades\Auth; // Function to reset button state if validation fails function resetSubmitButton() { - $(".button-save").attr("disabled", false); - $(".button-save").removeClass("disabled"); + $("#btn-save-opname").attr("disabled", false); + $("#btn-save-opname").removeClass("disabled"); updateProductCounter(); // Update with current counter } @@ -1033,6 +1175,18 @@ use Illuminate\Support\Facades\Auth; $('#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 if ($('.is-invalid').length > 0) { // Scroll to first error @@ -1043,7 +1197,7 @@ use Illuminate\Support\Facades\Auth; // Show alert for validation errors @if(session('error')) Swal.fire({ - icon: 'error', + type: 'error', title: 'Terjadi Kesalahan', text: '{{ session("error") }}', confirmButtonText: 'OK' @@ -1051,10 +1205,94 @@ use Illuminate\Support\Facades\Auth; @endif } - // Auto-dismiss alerts after 5 seconds - setTimeout(function() { - $('.alert').fadeOut('slow'); - }, 5000); + + + // Handle success/error messages for both opname and mutasi + @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(` + + + + + + - + + + + + + + + + `); + 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('
'), + confirmButtonText: 'OK' + }); + @endif }); // Handle opname date field - set default if becomes empty @@ -1065,19 +1303,178 @@ use Illuminate\Support\Facades\Auth; } }); - // Handle mutasi product selection - $(document).on('change', '.mutasi-product-checkbox', function() { - var quantityInput = $(this).closest('tr').find('.mutasi-quantity'); - if ($(this).is(':checked')) { - quantityInput.prop('disabled', false); + // Handle mutasi form - similar to create mutation + var productIndexMutasi = 0; + + + + // 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(' 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 = ` + + + + + + - + + + + + + + + + `; + + $('#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 { - 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 - $('#select-all-mutasi').change(function() { - $('.mutasi-product-checkbox').prop('checked', this.checked).trigger('change'); + + // Validate quantity against available stock for mutasi + $(document).on('input', '.quantity-input-mutasi', function() { + 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('
Quantity melebihi stock tersedia
'); + } + } 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'); + } }); diff --git a/resources/views/warehouse_management/mutations/_action.blade.php b/resources/views/warehouse_management/mutations/_action.blade.php index 468cf7e..dcb3e11 100644 --- a/resources/views/warehouse_management/mutations/_action.blade.php +++ b/resources/views/warehouse_management/mutations/_action.blade.php @@ -1,7 +1,7 @@
+ class="btn btn-sm btn-info mr-2"> Detail @@ -9,7 +9,7 @@ @if(auth()->user()->dealer_id == $row->to_dealer_id) @@ -31,7 +31,7 @@ @if(auth()->user()->dealer_id == $row->from_dealer_id || auth()->user()->hasRole('admin')) @endcan @endif - - @if(in_array($row->status->value, ['pending', 'approved']) && auth()->user()->id === $row->requested_by) - - - Edit - - @endif
@@ -88,7 +80,6 @@