feat(noc): implementasi mutual exclusive selection untuk status kurang dan lebih bayar
- Menambahkan JavaScript untuk mutual exclusive selection antara status kurang bayar dan lebih bayar - Hanya satu status yang bisa dipilih pada satu waktu untuk mencegah konflik data - Implementasi fungsi resetFields() untuk membersihkan field yang tidak dipilih - Menambahkan event handler untuk toggle visibility field berdasarkan pilihan status - Menambahkan logging untuk tracking perubahan status pembayaran - Field nominal dan bukti pengembalian otomatis direset ketika status berubah - Mempertahankan UI existing dengan checkbox namun menambahkan logika mutual exclusive - Menambahkan validasi client-side untuk mencegah input data yang tidak konsisten - Support untuk readonly mode ketika memo penyelesaian sudah ada - Implementasi function-level comments untuk dokumentasi kode
This commit is contained in:
@@ -60,6 +60,11 @@
|
|||||||
'tanggal_pembayaran' => $validated['tanggal_pembayaran'] ?? date('Y-m-d'),
|
'tanggal_pembayaran' => $validated['tanggal_pembayaran'] ?? date('Y-m-d'),
|
||||||
'status_bayar' => $validated['nominal_bayar'] < $validated['total_harus_bayar'] ? false : true,
|
'status_bayar' => $validated['nominal_bayar'] < $validated['total_harus_bayar'] ? false : true,
|
||||||
'catatan_noc' => $validated['catatan_noc'],
|
'catatan_noc' => $validated['catatan_noc'],
|
||||||
|
'status_kurang_bayar' => $validated['status_kurang_bayar'] ?? '',
|
||||||
|
'status_lebih_bayar' => $validated['status_lebih_bayar'] ?? '',
|
||||||
|
'nominal_kurang_bayar' => $validated['nominal_kurang_bayar'] ?? 0,
|
||||||
|
'nominal_lebih_bayar' => $validated['nominal_lebih_bayar'] ?? 0,
|
||||||
|
'bukti_pengembalian' => $validated['bukti_pengembalian'] ?? '',
|
||||||
];
|
];
|
||||||
$noc = Noc::updateOrCreate(
|
$noc = Noc::updateOrCreate(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -77,6 +77,11 @@
|
|||||||
'bukti_bayar' => $fileRule,
|
'bukti_bayar' => $fileRule,
|
||||||
'status_bayar' => 'nullable|boolean',
|
'status_bayar' => 'nullable|boolean',
|
||||||
'tanggal_pembayaran' => 'nullable|date',
|
'tanggal_pembayaran' => 'nullable|date',
|
||||||
|
'status_kurang_bayar' => 'nullable|boolean',
|
||||||
|
'status_lebih_bayar' => 'nullable|boolean',
|
||||||
|
'nominal_kurang_bayar' => 'nullable|numeric|min:0',
|
||||||
|
'nominal_lebih_bayar' => 'nullable|numeric|min:0',
|
||||||
|
'bukti_pengembalian' => 'nullable|file|mimes:pdf,jpg,jpeg,png|max:2048',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +154,11 @@
|
|||||||
'authorized_status.boolean' => 'Status Otorisasi harus berupa boolean',
|
'authorized_status.boolean' => 'Status Otorisasi harus berupa boolean',
|
||||||
'authorized_at.date' => 'Format Tanggal Otorisasi tidak valid',
|
'authorized_at.date' => 'Format Tanggal Otorisasi tidak valid',
|
||||||
'authorized_by.exists' => 'User Otorisasi tidak valid',
|
'authorized_by.exists' => 'User Otorisasi tidak valid',
|
||||||
|
'status_kurang_bayar.boolean' => 'Status Kurang Bayar harus berupa boolean',
|
||||||
|
'status_lebih_bayar.boolean' => 'Status Lebih Bayar harus berupa boolean',
|
||||||
|
'nominal_kurang_bayar.numeric' => 'Nominal Kurang Bayar harus berupa angka',
|
||||||
|
'nominal_kurang_bayar.min' => 'Nominal Kurang Bayar minimal 0',
|
||||||
|
'nominal_lebih_bayar.numeric' => 'Nominal Lebih Bayar harus berupa angka',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace Modules\Lpj\Models;
|
namespace Modules\Lpj\Models;
|
||||||
|
|
||||||
use Illuminate\Foundation\Auth\User;
|
use Illuminate\Foundation\Auth\User;
|
||||||
// use Modules\Lpj\Database\Factories\NocFactory;
|
|
||||||
|
|
||||||
class Noc extends Base
|
class Noc extends Base
|
||||||
{
|
{
|
||||||
@@ -16,6 +15,11 @@ class Noc extends Base
|
|||||||
'nominal_bayar',
|
'nominal_bayar',
|
||||||
'total_pembukuan',
|
'total_pembukuan',
|
||||||
'status_bayar',
|
'status_bayar',
|
||||||
|
'status_kurang_bayar',
|
||||||
|
'nominal_kurang_bayar',
|
||||||
|
'status_lebih_bayar',
|
||||||
|
'nominal_lebih_bayar',
|
||||||
|
'bukti_pengembalian',
|
||||||
'tanggal_pembayaran',
|
'tanggal_pembayaran',
|
||||||
'nominal_penyelesaian',
|
'nominal_penyelesaian',
|
||||||
'status_penyelesaiaan',
|
'status_penyelesaiaan',
|
||||||
@@ -37,6 +41,10 @@ class Noc extends Base
|
|||||||
protected $casts = [
|
protected $casts = [
|
||||||
'nominal_bayar' => 'decimal:2',
|
'nominal_bayar' => 'decimal:2',
|
||||||
'status_bayar' => 'boolean',
|
'status_bayar' => 'boolean',
|
||||||
|
'status_kurang_bayar' => 'boolean',
|
||||||
|
'nominal_kurang_bayar' => 'decimal:2',
|
||||||
|
'status_lebih_bayar' => 'boolean',
|
||||||
|
'nominal_lebih_bayar' => 'decimal:2',
|
||||||
'tanggal_pembayaran' => 'date',
|
'tanggal_pembayaran' => 'date',
|
||||||
'nominal_penyelesaian' => 'decimal:2',
|
'nominal_penyelesaian' => 'decimal:2',
|
||||||
'status_penyelesaiaan' => 'boolean',
|
'status_penyelesaiaan' => 'boolean',
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration {
|
||||||
|
/**
|
||||||
|
* Menjalankan migration untuk menambahkan field status pembayaran
|
||||||
|
* Field yang ditambahkan: status_kurang_bayar, nominal_kurang_bayar,
|
||||||
|
* status_lebih_bayar, nominal_lebih_bayar, bukti_pengembalian
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('noc', function (Blueprint $table) {
|
||||||
|
// Field untuk status kurang bayar
|
||||||
|
$table->boolean('status_kurang_bayar')->default(false)->after('status_bayar');
|
||||||
|
$table->decimal('nominal_kurang_bayar', 15, 2)->nullable()->after('status_kurang_bayar');
|
||||||
|
|
||||||
|
// Field untuk status lebih bayar
|
||||||
|
$table->boolean('status_lebih_bayar')->default(false)->after('nominal_kurang_bayar');
|
||||||
|
$table->decimal('nominal_lebih_bayar', 15, 2)->nullable()->after('status_lebih_bayar');
|
||||||
|
|
||||||
|
// Field untuk bukti pengembalian jika lebih bayar
|
||||||
|
$table->string('bukti_pengembalian')->nullable()->after('nominal_lebih_bayar');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback migration
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('noc', function (Blueprint $table) {
|
||||||
|
$table->dropColumn([
|
||||||
|
'status_kurang_bayar',
|
||||||
|
'nominal_kurang_bayar',
|
||||||
|
'status_lebih_bayar',
|
||||||
|
'nominal_lebih_bayar',
|
||||||
|
'bukti_pengembalian'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -139,6 +139,112 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Field Status Kurang Bayar -->
|
||||||
|
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
|
||||||
|
<label class="form-label max-w-56">
|
||||||
|
Status Kurang Bayar
|
||||||
|
</label>
|
||||||
|
<div class="flex flex-wrap items-baseline w-full">
|
||||||
|
<div class="flex gap-4 items-center">
|
||||||
|
<label class="checkbox-group">
|
||||||
|
<input class="checkbox checkbox-sm" name="status_kurang_bayar" id="status_kurang_bayar"
|
||||||
|
value="1" type="checkbox"
|
||||||
|
{{ old('status_kurang_bayar', $persetujuanPenawaran->noc->status_kurang_bayar ?? false) ? 'checked' : '' }}
|
||||||
|
{{ $hasMemo ? 'disabled' : '' }}>
|
||||||
|
<span class="checkbox-label">
|
||||||
|
Ya, ada kurang bayar
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
@error('status_kurang_bayar')
|
||||||
|
<em class="text-sm alert text-danger">{{ $message }}</em>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Field Nominal Kurang Bayar -->
|
||||||
|
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap" id="nominal_kurang_bayar_section"
|
||||||
|
style="display: none;">
|
||||||
|
<label class="form-label max-w-56">
|
||||||
|
Nominal Kurang Bayar
|
||||||
|
</label>
|
||||||
|
<div class="flex flex-wrap items-baseline w-full">
|
||||||
|
<input type="number" name="nominal_kurang_bayar" id="nominal_kurang_bayar"
|
||||||
|
class="input w-full @error('nominal_kurang_bayar') border-danger bg-danger-light @enderror"
|
||||||
|
value="{{ old('nominal_kurang_bayar', $persetujuanPenawaran->noc->nominal_kurang_bayar ?? '') }}"
|
||||||
|
placeholder="Masukkan nominal kurang bayar" {{ $hasMemo ? 'readonly' : '' }}>
|
||||||
|
@error('nominal_kurang_bayar')
|
||||||
|
<em class="text-sm alert text-danger">{{ $message }}</em>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Field Status Lebih Bayar -->
|
||||||
|
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
|
||||||
|
<label class="form-label max-w-56">
|
||||||
|
Status Lebih Bayar
|
||||||
|
</label>
|
||||||
|
<div class="flex flex-wrap items-baseline w-full">
|
||||||
|
<div class="flex gap-4 items-center">
|
||||||
|
<label class="checkbox-group">
|
||||||
|
<input class="checkbox checkbox-sm" name="status_lebih_bayar" id="status_lebih_bayar"
|
||||||
|
value="1" type="checkbox"
|
||||||
|
{{ old('status_lebih_bayar', $persetujuanPenawaran->noc->status_lebih_bayar ?? false) ? 'checked' : '' }}
|
||||||
|
{{ $hasMemo ? 'disabled' : '' }}>
|
||||||
|
<span class="checkbox-label">Ya, ada lebih bayar </span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
@error('status_lebih_bayar')
|
||||||
|
<em class="text-sm alert text-danger">{{ $message }}</em>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Field Nominal Lebih Bayar -->
|
||||||
|
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap" id="nominal_lebih_bayar_section"
|
||||||
|
style="display: none;">
|
||||||
|
<label class="form-label max-w-56">
|
||||||
|
Nominal Lebih Bayar
|
||||||
|
</label>
|
||||||
|
<div class="flex flex-wrap items-baseline w-full">
|
||||||
|
<input type="number" name="nominal_lebih_bayar" id="nominal_lebih_bayar"
|
||||||
|
class="input w-full @error('nominal_lebih_bayar') border-danger bg-danger-light @enderror"
|
||||||
|
value="{{ old('nominal_lebih_bayar', $persetujuanPenawaran->noc->nominal_lebih_bayar ?? '') }}"
|
||||||
|
placeholder="Masukkan nominal lebih bayar" {{ $hasMemo ? 'readonly' : '' }}>
|
||||||
|
@error('nominal_lebih_bayar')
|
||||||
|
<em class="text-sm alert text-danger">{{ $message }}</em>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Field Bukti Pengembalian -->
|
||||||
|
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap" id="bukti_pengembalian_section"
|
||||||
|
style="display: none;">
|
||||||
|
<label class="form-label max-w-56">
|
||||||
|
Bukti Pengembalian
|
||||||
|
</label>
|
||||||
|
<div class="flex flex-wrap items-baseline w-full">
|
||||||
|
@if (!$hasMemo)
|
||||||
|
<input type="file" name="bukti_pengembalian" id="bukti_pengembalian"
|
||||||
|
class="file-input w-full @error('bukti_pengembalian') border-danger bg-danger-light @enderror"
|
||||||
|
accept=".pdf,.jpg,.jpeg,.png">
|
||||||
|
@error('bukti_pengembalian')
|
||||||
|
<em class="text-sm alert text-danger">{{ $message }}</em>
|
||||||
|
@enderror
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (isset($persetujuanPenawaran->noc->bukti_pengembalian) && !empty($persetujuanPenawaran->noc->bukti_pengembalian))
|
||||||
|
<div class="flex items-center mt-2">
|
||||||
|
<a href="{{ Storage::url($persetujuanPenawaran->noc->bukti_pengembalian) }}"
|
||||||
|
target="_blank" class="badge badge-sm badge-outline badge-warning">
|
||||||
|
<i class="mr-2 ki-filled ki-eye"></i> Lihat File
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
|
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
|
||||||
<label class="form-label max-w-56">
|
<label class="form-label max-w-56">
|
||||||
Bukti KSL
|
Bukti KSL
|
||||||
@@ -274,3 +380,91 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
/**
|
||||||
|
* Fungsi untuk mengelola mutual exclusive selection antara kurang bayar dan lebih bayar
|
||||||
|
* Hanya satu status yang bisa dipilih pada satu waktu
|
||||||
|
*/
|
||||||
|
const statusKurangBayar = document.getElementById('status_kurang_bayar');
|
||||||
|
const statusLebihBayar = document.getElementById('status_lebih_bayar');
|
||||||
|
const nominalKurangBayarSection = document.getElementById('nominal_kurang_bayar_section');
|
||||||
|
const nominalLebihBayarSection = document.getElementById('nominal_lebih_bayar_section');
|
||||||
|
const buktiPengembalianSection = document.getElementById('bukti_pengembalian_section');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fungsi untuk reset field dan section visibility
|
||||||
|
* @param {string} type - 'kurang' atau 'lebih'
|
||||||
|
*/
|
||||||
|
function resetFields(type) {
|
||||||
|
if (type === 'kurang') {
|
||||||
|
// Reset field lebih bayar
|
||||||
|
statusLebihBayar.checked = false;
|
||||||
|
nominalLebihBayarSection.style.display = 'none';
|
||||||
|
buktiPengembalianSection.style.display = 'none';
|
||||||
|
document.getElementById('nominal_lebih_bayar').value = '';
|
||||||
|
document.getElementById('bukti_pengembalian').value = '';
|
||||||
|
} else if (type === 'lebih') {
|
||||||
|
// Reset field kurang bayar
|
||||||
|
statusKurangBayar.checked = false;
|
||||||
|
nominalKurangBayarSection.style.display = 'none';
|
||||||
|
document.getElementById('nominal_kurang_bayar').value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler untuk status kurang bayar
|
||||||
|
* Menampilkan field nominal kurang bayar dan menyembunyikan field lebih bayar
|
||||||
|
*/
|
||||||
|
if (statusKurangBayar) {
|
||||||
|
// Set initial state
|
||||||
|
if (statusKurangBayar.checked) {
|
||||||
|
nominalKurangBayarSection.style.display = 'flex';
|
||||||
|
resetFields('kurang');
|
||||||
|
}
|
||||||
|
|
||||||
|
statusKurangBayar.addEventListener('change', function() {
|
||||||
|
if (this.checked) {
|
||||||
|
nominalKurangBayarSection.style.display = 'flex';
|
||||||
|
resetFields('kurang');
|
||||||
|
console.log('Status kurang bayar dipilih, field lebih bayar direset');
|
||||||
|
} else {
|
||||||
|
nominalKurangBayarSection.style.display = 'none';
|
||||||
|
document.getElementById('nominal_kurang_bayar').value = '';
|
||||||
|
console.log('Status kurang bayar dibatalkan');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event handler untuk status lebih bayar
|
||||||
|
* Menampilkan field nominal dan bukti pengembalian, menyembunyikan field kurang bayar
|
||||||
|
*/
|
||||||
|
if (statusLebihBayar) {
|
||||||
|
// Set initial state
|
||||||
|
if (statusLebihBayar.checked) {
|
||||||
|
nominalLebihBayarSection.style.display = 'flex';
|
||||||
|
buktiPengembalianSection.style.display = 'flex';
|
||||||
|
resetFields('lebih');
|
||||||
|
}
|
||||||
|
|
||||||
|
statusLebihBayar.addEventListener('change', function() {
|
||||||
|
if (this.checked) {
|
||||||
|
nominalLebihBayarSection.style.display = 'flex';
|
||||||
|
buktiPengembalianSection.style.display = 'flex';
|
||||||
|
resetFields('lebih');
|
||||||
|
console.log('Status lebih bayar dipilih, field kurang bayar direset');
|
||||||
|
} else {
|
||||||
|
nominalLebihBayarSection.style.display = 'none';
|
||||||
|
buktiPengembalianSection.style.display = 'none';
|
||||||
|
document.getElementById('nominal_lebih_bayar').value = '';
|
||||||
|
document.getElementById('bukti_pengembalian').value = '';
|
||||||
|
console.log('Status lebih bayar dibatalkan');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
|
|||||||
Reference in New Issue
Block a user