Files
CKB/resources/views/warehouse_management/mutations/create.blade.php

442 lines
17 KiB
PHP
Executable File

@extends('layouts.backapp')
@section('content')
<div class="row">
<div class="col-lg-12">
<div class="kt-portlet">
<div class="kt-portlet__head">
<div class="kt-portlet__head-label">
<h3 class="kt-portlet__head-title">
Tambah Mutasi Baru
</h3>
</div>
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-wrapper">
<div class="kt-portlet__head-actions">
<a href="{{ route('mutations.index') }}" class="btn btn-secondary">
<i class="la la-arrow-left"></i>
Kembali
</a>
</div>
</div>
</div>
</div>
<form action="{{ route('mutations.store') }}" method="POST" id="mutation-form">
@csrf
<div class="kt-portlet__body">
@if ($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="from_dealer_id">Dealer Asal <span class="text-danger">*</span></label>
<select name="from_dealer_id" id="from_dealer_id" class="form-control select2" required>
<option value="">Pilih Dealer Asal</option>
@foreach($dealers as $dealer)
<option value="{{ $dealer->id }}" {{ old('from_dealer_id') == $dealer->id ? 'selected' : '' }}>
{{ $dealer->name }}
</option>
@endforeach
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="to_dealer_id">Dealer Tujuan <span class="text-danger">*</span></label>
<select name="to_dealer_id" id="to_dealer_id" class="form-control select2" required>
<option value="">Pilih Dealer Tujuan</option>
@foreach($dealers as $dealer)
<option value="{{ $dealer->id }}" {{ old('to_dealer_id') == $dealer->id ? 'selected' : '' }}>
{{ $dealer->name }}
</option>
@endforeach
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="shipping_notes">Catatan Pengiriman</label>
<textarea name="shipping_notes" id="shipping_notes" class="form-control" rows="3"
placeholder="Catatan khusus untuk pengiriman mutasi ini (opsional)">{{ old('shipping_notes') }}</textarea>
<small class="form-text text-muted">Catatan ini akan dilihat oleh dealer penerima</small>
</div>
</div>
</div>
<div class="kt-separator kt-separator--border-dashed kt-separator--space-lg"></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">
<i class="la la-plus"></i> Tambah Produk
</button>
</div>
<div class="table-responsive">
<table class="table table-bordered" id="products-table">
<thead>
<tr>
<th width="40%">Produk</th>
<th width="20%">Stock Tersedia</th>
<th width="25%">Quantity</th>
<th width="15%">Aksi</th>
</tr>
</thead>
<tbody id="products-tbody">
<tr class="product-row" data-index="0">
<td>
<select name="products[0][product_id]" class="form-control product-select select2" 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 text-muted">-</span>
</td>
<td>
<input type="number"
name="products[0][quantity_requested]"
class="form-control quantity-input"
min="0.01"
step="0.01"
placeholder="0"
required>
</td>
<td>
<button type="button" class="btn btn-danger btn-sm remove-product" disabled>
<i class="la la-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="alert alert-info">
<i class="la la-info-circle"></i>
<strong>Informasi:</strong>
Mutasi akan dikirim ke dealer tujuan. Catatan dapat ditambahkan saat dealer tujuan menerima mutasi.
</div>
</div>
<div class="kt-portlet__foot">
<div class="kt-form__actions kt-form__actions--right">
<button type="submit" class="btn btn-primary" id="submit-btn">
Simpan Mutasi
</button>
<button type="button" class="btn btn-secondary" onclick="window.history.back()">
Batal
</button>
</div>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('javascripts')
<script>
$(document).ready(function () {
let productIndex = 1;
let originalProductOptions = ""; // Store original product options
// Initialize Select2
$(".select2").select2({
placeholder: "Pilih...",
allowClear: true,
});
// Store original product options on page load
const firstSelect = $(".product-select").first();
if (firstSelect.length > 0) {
originalProductOptions = firstSelect.html();
}
// Prevent same dealer selection
$("#from_dealer_id, #to_dealer_id").on("change", function () {
const fromDealerId = $("#from_dealer_id").val();
const toDealerId = $("#to_dealer_id").val();
if (fromDealerId && toDealerId && fromDealerId === toDealerId) {
$(this).val("").trigger("change");
Swal.fire({
type: "error",
title: "Oops...",
text: "Dealer asal dan tujuan tidak boleh sama",
});
return false;
}
// Update available stock when dealer changes
updateAllAvailableStock();
});
// Add new product row
$("#add-product").on("click", function () {
const newRow = createProductRow(productIndex);
$("#products-tbody").append(newRow);
// Initialize Select2 for new row after it's added to DOM
const newSelect = $(
`#products-tbody tr[data-index="${productIndex}"] .product-select`
);
newSelect.select2({
placeholder: "Pilih...",
allowClear: true,
});
productIndex++;
updateRemoveButtons();
});
// Remove product row
$(document).on("click", ".remove-product", function () {
$(this).closest("tr").remove();
updateRemoveButtons();
reindexRows();
});
// Handle product selection change
$(document).on("change", ".product-select", function () {
const row = $(this).closest("tr");
const productId = $(this).val();
const fromDealerId = $("#from_dealer_id").val();
if (productId && fromDealerId) {
getAvailableStock(productId, fromDealerId, row);
} else {
row.find(".available-stock").text("-");
row.find(".quantity-input").attr("max", "");
}
});
// Validate quantity input
$(document).on("input", ".quantity-input", function () {
const maxValue = parseFloat($(this).attr("max"));
const currentValue = parseFloat($(this).val());
if (maxValue && currentValue > maxValue) {
$(this).val(maxValue);
$(this).addClass("is-invalid");
if (!$(this).siblings(".invalid-feedback").length) {
$(this).after(
'<div class="invalid-feedback">Quantity melebihi stock yang tersedia</div>'
);
}
} else {
$(this).removeClass("is-invalid");
$(this).siblings(".invalid-feedback").remove();
}
});
// Form submission
$("#mutation-form").on("submit", function (e) {
e.preventDefault();
if (!validateForm()) {
return false;
}
const submitBtn = $("#submit-btn");
const originalText = submitBtn.html();
submitBtn
.prop("disabled", true)
.html('<i class="la la-spinner la-spin"></i> Menyimpan...');
// Submit form
this.submit();
});
function createProductRow(index) {
return `
<tr class="product-row" data-index="${index}">
<td>
<select name="products[${index}][product_id]" class="form-control product-select" required>
${originalProductOptions}
</select>
</td>
<td class="text-center">
<span class="available-stock text-muted">-</span>
</td>
<td>
<input type="number"
name="products[${index}][quantity_requested]"
class="form-control quantity-input"
min="0.01"
step="0.01"
placeholder="0"
required>
</td>
<td>
<button type="button" class="btn btn-danger btn-sm remove-product">
<i class="la la-trash"></i>
</button>
</td>
</tr>
`;
}
function updateRemoveButtons() {
const rows = $(".product-row");
$(".remove-product").prop("disabled", rows.length <= 1);
}
function reindexRows() {
$(".product-row").each(function (index) {
$(this).attr("data-index", index);
$(this)
.find('select[name*="product_id"]')
.attr("name", `products[${index}][product_id]`);
$(this)
.find('input[name*="quantity_requested"]')
.attr("name", `products[${index}][quantity_requested]`);
});
productIndex = $(".product-row").length;
}
function getAvailableStock(productId, dealerId, row) {
$.ajax({
url: "/warehouse/mutations/get-product-stock",
method: "GET",
data: {
product_id: productId,
dealer_id: dealerId,
},
beforeSend: function () {
row.find(".available-stock").html(
'<i class="la la-spinner la-spin"></i>'
);
},
success: function (response) {
const stock = parseFloat(response.current_stock);
row.find(".available-stock").text(stock.toLocaleString());
row.find(".quantity-input").attr("max", stock);
// Set max value message
if (stock <= 0) {
row.find(".available-stock")
.addClass("text-danger")
.removeClass("text-muted");
row.find(".quantity-input").attr("readonly", true).val("");
} else {
row.find(".available-stock")
.removeClass("text-danger")
.addClass("text-muted");
row.find(".quantity-input").attr("readonly", false);
}
},
error: function () {
row.find(".available-stock")
.text("Error")
.addClass("text-danger");
},
});
}
function updateAllAvailableStock() {
const fromDealerId = $("#from_dealer_id").val();
$(".product-row").each(function () {
const row = $(this);
const productId = row.find(".product-select").val();
if (productId && fromDealerId) {
getAvailableStock(productId, fromDealerId, row);
} else {
row.find(".available-stock").text("-");
row.find(".quantity-input").attr("max", "");
}
});
}
function validateForm() {
let isValid = true;
const fromDealerId = $("#from_dealer_id").val();
const toDealerId = $("#to_dealer_id").val();
// Check dealers
if (!fromDealerId) {
Swal.fire({
type: "error",
title: "Oops...",
text: "Pilih dealer asal",
});
return false;
}
if (!toDealerId) {
Swal.fire({
type: "error",
title: "Oops...",
text: "Pilih dealer tujuan",
});
return false;
}
if (fromDealerId === toDealerId) {
Swal.fire({
type: "error",
title: "Oops...",
text: "Dealer asal dan tujuan tidak boleh sama",
});
return false;
}
// Check products
const productRows = $(".product-row");
if (productRows.length === 0) {
Swal.fire({
type: "error",
title: "Oops...",
text: "Tambahkan minimal satu produk",
});
return false;
}
let hasValidProduct = false;
productRows.each(function () {
const productId = $(this).find(".product-select").val();
const quantity = $(this).find(".quantity-input").val();
if (productId && quantity && parseFloat(quantity) > 0) {
hasValidProduct = true;
}
});
if (!hasValidProduct) {
Swal.fire({
type: "error",
title: "Oops...",
text: "Pilih minimal satu produk dengan quantity yang valid",
});
return false;
}
return isValid;
}
});
</script>
@endsection