Compare commits

..

51 Commits

Author SHA1 Message Date
Daeng Deni Mardaeni
45cebcf325 🔧(services): Selaraskan fallback path foto pada PreviewLaporanService dan tambah logging
- Menyelaraskan resolusi path file foto di PreviewLaporanService dengan logika fallback seperti pada komponen foto:
  - Menggunakan `originalPath = $path['path']` sebagai path utama
  - Jika `statusLpj == 1` dan file asli tidak ditemukan, maka:
    - Ekstrak dua bagian terakhir dari path (contoh: `251051/251051_2_2.png`)
    - Bangun `fallbackPath` dengan pola `surveyor/001/{lastTwoParts}`
  - Tentukan `pathToUse`:
    - Apabila `fallbackPath` ada dan file fallback ditemukan, gunakan `fallbackPath`
    - Jika tidak, tetap gunakan `originalPath`
  - Resolusi final: `storage_path('app/public/' . $pathToUse)`

- Menambahkan logging untuk setiap operasi penting dan setiap titik pengembalian:
  - `Log::warning` saat path kosong terdeteksi dalam daftar paths
  - `Log::info` saat fallback kandidat dibangun dari `originalPath`
  - `Log::warning` saat file tidak ditemukan pada original maupun fallback (dengan menyertakan ketiganya: original, fallback, resolved)
  - `Log::info` saat file berhasil ditambahkan ke daftar unduhan
  - `Log::warning` saat tidak ada file valid setelah resolusi path
  - `Log::info` saat mengunduh single file
  - `Log::info` saat zip file berhasil dibuat (dengan jumlah file)
  - `Log::error` saat zip file gagal dibuat

- Memindahkan logika resolusi path di dalam loop pengolahan `$paths` untuk setiap item:
  - Mengganti setting langsung `storage_path('app/public/' . $path['path'])` dengan resolusi path yang bisa fallback
  - Menjaga struktur kontrol dengan `continue` jika file final tetap tidak ditemukan

- Menambahkan komentar level-fungsi (docblock) pada `previewLaporan`:
  - Menjelaskan tanggung jawab fungsi dalam menghasilkan PDF atau unduhan foto
  - Menjelaskan skenario fallback path dan tujuan mengurangi gambar hilang
  - Mendokumentasikan parameter dan bentuk nilai kembali respons
2025-11-07 10:57:46 +07:00
Daeng Deni Mardaeni
a442f879b7 🔧(services): Perbaikan logika preview laporan dan format saran pada print-out
- **PreviewLaporanService.php**:
  - Menambahkan parsing JSON untuk mig_permohonan sebelum digunakan
  - Memperbaiki kondisi untuk menentukan statusLpj dengan menambahkan validasi nilai pasar < 1
  - Meningkatkan logika penentuan path foto berdasarkan status memo dan nilai total pasar wajar

- **print-out-standar.blade.php**:
  - Memindahkan header tabel 'SARAN' keluar dari loop foreach untuk mencegah duplikasi header
  - Menambahkan format list (ul/li) pada tampilan saran untuk meningkatkan readability
  - Menambahkan margin-top 20px pada section penutup laporan untuk spacing yang lebih baik
  - Memperbaiki struktur HTML dengan wrapping konten saran dalam list item
2025-10-31 10:07:02 +07:00
Daeng Deni Mardaeni
796cb89d21 🔧(penilai): Perbaikan layout dan validasi data pada komponen laporan
- **foto-jaminan.blade.php**: Memindahkan page-break ke awal chunk (kecuali chunk pertama) untuk mencegah page break yang tidak perlu di akhir
- **print-out-sederhana.blade.php**:
  - Menambahkan validasi isset() untuk mencegah error undefined index pada hub_cadeb
  - Menambahkan prefix 'Lain-lain, ' untuk hubungan debitur yang tidak sesuai
  - Menambahkan guard clause untuk mencegah error saat SARANA PELENGKAP DAN LINGKUNGAN tidak ada
  - Menambahkan guard clause untuk ANALISA TANAH DAN BANGUNAN
- **print-out-standar.blade.php**:
  - Menambahkan validasi null check untuk mig_detail_legalitas_jaminan
  - Memperbaiki indentasi dan struktur kode untuk konsistensi

Perubahan ini meningkatkan stabilitas aplikasi dengan mencegah error undefined index dan memperbaiki layout PDF yang dihasilkan dengan penempatan page break yang lebih tepat.
2025-10-30 14:00:49 +07:00
Daeng Deni Mardaeni
fb22d370b5 🔧(penilai): Perbaikan tampilan dan logika fallback foto jaminan
Implementasi berbagai perbaikan pada komponen penilai dan foto jaminan:

**Detail Lokasi (detail-lokasi.blade.php):**
- Menambahkan logika untuk menghindari duplikasi label yang sama
- Implementasi tracking currentKey untuk mencegah label berulang
- Perbaikan tampilan tabel dengan label yang lebih bersih

**Footer (footer.blade.php):**
- Perbaikan indentasi dan formatting kode
- Reorganisasi array customLabels untuk konsistensi
- Perbaikan struktur conditional untuk upload_gs

**Foto Jaminan (foto-jaminan.blade.php):**
- Implementasi sistem fallback path untuk file foto yang tidak ditemukan
- Logika fallback: surveyor/2025/APRIL/26042025/251051/251051_2_2.png → surveyor/001/251051/251051_2_2.png
- Penambahan urutan kategori foto yang diinginkan (BLAD TATA KOTA sebelum FOTO JAMINAN)
- Skip kategori DOKUMEN PENDUKUNG dari tampilan utama
- Perbaikan class ordering (border photo-item)
- Update alt attribute untuk menggunakan pathToUse
- Penambahan debug @dd untuk otherPhotos (sementara)

**Print Out Sederhana (print-out-sederhana.blade.php):**
- Perbaikan tampilan Total Nilai Pasar Wajar dengan colspan yang benar
- Restructuring tabel NPW tambahan dengan width yang tepat
- Perbaikan alignment dan formatting nilai
- Update parameter signature-approval dengan npw
- Perbaikan tampilan Total Nilai Likuidasi
- Penambahan tanda '--' pada SARANA PELENGKAP DAN LINGKUNGAN

**Print Out Standar (print-out-standar.blade.php):**
- Implementasi logika untuk menghindari duplikasi label
- Perbaikan struktur tabel dengan width 100%
- Update tracking currentLabel untuk konsistensi tampilan
2025-10-29 10:50:13 +07:00
Daeng Deni Mardaeni
478c0c8079 🔧(helpers): Tambah fungsi parseTimestamp & perbaiki parsing tanggal
- Tambah fungsi parseTimestamp di app/Helpers/Lpj.php untuk parsing timestamp yang robust
- Gunakan native PHP DateTime & fallback ke Carbon untuk multiple format
- Validasi lengkap day, month, year, hour, minute, second dengan range aman
- Support format: Y-m-d H:i:s, Y-m-d, d/m/Y H:i:s, d/m/Y, d-m-Y, j-n-Y, dll
- Tambah logging detail & trim whitespace untuk reliability tinggi
- Return selalu format Y-m-d H:i:s agar konsisten di seluruh sistem
- signature-approval.blade.php gunakan parseTimestamp() untuk mig_mst_jaminan_tgl_laporan
- Tanggal migrasi kini diparsing & diformat dengan lebih akurat
- Hasil: parsing timestamp konsisten, error handling lebih kuat, dan log lebih informatif
2025-10-28 14:02:50 +07:00
Daeng Deni Mardaeni
73d0d238c0 🔧(penilai): Perbaiki total nilai & signature approval
- Gabungkan header 'Total Nilai Pasar Wajar' dengan colspan=2
- Gunakan nilai likuidasi default 70% & data migrasi mig_mst_lpj_tot_nilai_likuidasi
- Tambah parameter laporan='sederhana' pada include signature-approval
- Perbaiki layout tabel di print-out-sederhana & print-out-standar
- Tambah parsing mig_permohonan dengan json_decode untuk data migrasi
- Sesuaikan width kolom agar tampilan lebih proporsional
- Hilangkan Carbon::createFromFormat & tambah kondisi laporan di signature-approval
- Tampilkan SENIOR OFFICER untuk laporan sederhana, EO untuk standar
2025-10-28 13:40:11 +07:00
Daeng Deni Mardaeni
d6d0ed28b9 🔧(penilai): Perbaiki logika approval & tanggal laporan
- Nonaktifkan filter dokument_id di PreviewLaporanService untuk fleksibilitas
- Tambah kondisi tampilkan DD hanya jika berbeda dengan EO
- Gunakan tanggal mig_mst_jaminan_tgl_laporan sebagai sumber utama
- Perbaiki logika nama & tanggal approval SO
- Gunakan Carbon::createFromFormat untuk parsing tanggal migrasi
- Tambah validasi kondisional agar tidak duplikasi approval
- Rapikan struktur HTML & indentasi signature-approval.blade.php
- Pastikan konsistensi data antara preview service & signature view
- Hasil: tampilan signature lebih akurat & sesuai data migrasi
2025-10-28 12:33:25 +07:00
Daeng Deni Mardaeni
ee828455a9 🗑️(penilai): Menghapus section perhitungan nilai pasar wajar otomatis
Menghapus loop perhitungan nilai pasar wajar otomatis dari labelNilai pada komponen print-out laporan untuk memberikan kontrol manual yang lebih baik.

Perubahan yang dilakukan:

1. Print-out Sederhana (print-out-sederhana.blade.php)
- Menghapus foreach loop untuk labelNilai yang menampilkan perhitungan otomatis
- Menghapus 18 baris kode yang menghandle luas dan nilai per kategori
- Mempertahankan section npw_tambahan dengan perbaikan styling width
- Memperbaiki alignment dan width konsistensi pada tabel

2. Print-out Standar (print-out-standar.blade.php)
- Menghapus foreach loop untuk labelNilai yang sama
- Menghapus 18 baris kode perhitungan otomatis nilai pasar wajar
- Mempertahankan section npw_tambahan dengan styling yang diperbaiki
- Memperbaiki width property pada td element (menambahkan spasi)
2025-10-27 18:18:51 +07:00
Daeng Deni Mardaeni
a6481dd482 🔧(penilai): Perbaiki tampilan laporan & tambah fitur saran
- PreviewLaporanService.php:
  - Aktifkan kembali filter dokument_id untuk query Penilai
  - Tambahkan validasi dokumen ID agar data yang diambil tepat
  - Perbaiki logika pengambilan status LPJ untuk akurasi laporan

- informasi.blade.php:
  - Tambahkan section baru untuk menampilkan data saran dari form inspeksi
  - Implementasi loop untuk menampilkan banyak saran (array/string)
  - Styling tabel saran dengan border & alignment konsisten

- print-out-sederhana.blade.php:
  - Hilangkan koma awal di nilai dengan ltrim() untuk tampilan bersih
  - Konsistensi format antar section laporan

- print-out-standar.blade.php:
  - Tambah field 'Fasilitas Kredit' dan 'Alamat & Telepon Debitur'
  - Perbaiki logika total nilai pasar wajar dengan conditional formatting
  - Tambah section saran yang konsisten dengan komponen lain
  - Hapus semicolon berlebih (syntax fix)

- signature-approval.blade.php:
  - Ubah sumber tanggal dari updated_at ke tanggal_kunjungan untuk akurasi
2025-10-27 15:13:35 +07:00
Daeng Deni Mardaeni
cbf54353f4 (slik): Tambahkan halaman index SLIK untuk admin kredit
- Tambah halaman index SLIK di modul admin kredit untuk manajemen data Sistem Layanan Informasi Keuangan
- Buat tabel data dengan kolom: Sandi Bank, Tahun, No Rekening, CIF, Nama Debitur, Kolektibilitas, Fasilitas, Nilai Agunan, dan Status
- Implementasi pencarian keyword serta filter tahun, bulan, dan status agunan
- Tambahkan tombol aksi 'Detail' untuk melihat data lengkap
- Tambahkan fitur import dari Excel (.xlsx/.xls/.csv) dengan validasi format & file size ≤ 10MB
- Tambahkan fitur export Excel berdasarkan filter aktif
- Tambahkan tombol download template Excel untuk panduan format import
- Implementasi fungsi truncate/hapus semua data SLIK dengan modal konfirmasi
- Tambahkan validasi kombinasi unik (sandi bank, tahun, bulan, no rekening) untuk update data otomatis
- Gunakan KTDataTable dengan server-side processing, layout responsif, dan scroll horizontal
- Tambahkan modal import & modal konfirmasi hapus
- Tambahkan breadcrumbs, event listener untuk pencarian/filter, dan CSRF protection
- Implementasi validasi keamanan file upload dan pesan error/success
2025-10-23 16:59:47 +07:00
Daeng Deni Mardaeni
a701e78982 🔧 fix(LPJ): Perbaikan logika preview laporan dan optimasi import service
Perubahan utama pada PreviewLaporanService.php:
- Mengoptimalkan urutan import dengan mengelompokkan berdasarkan namespace
- Memperbaiki logika tanggal laporan untuk data migrasi dengan fallback ke tgl_create
- Menghapus filter dokument_id pada query LPJ untuk fleksibilitas
- Menambahkan penanganan khusus untuk tipe laporan 'memo' pada data migrasi
- Menambahkan validasi file path untuk download dokumen memo
- Memperbaiki kondisi pengecekan memo untuk non-migrasi data

Perubahan pada print-out-standar.blade.php:
- Menyembunyikan baris header dokumen yang menyebabkan duplikasi tampilan
- Menambahkan fungsi ltrim untuk membersihkan koma di awal nilai data

Perubahan ini meningkatkan stabilitas dan akurasi tampilan preview laporan,\nserta memperbaiki penanganan data migrasi dan memo khusus.
2025-10-23 14:35:12 +07:00
Daeng Deni Mardaeni
6305f93f0f fix(LPJ): Perbaikan label luas unit untuk jenis aset Apartemen
Mengubah label dari 'Luas Tanah' menjadi 'Luas Unit' untuk jenis aset Apartemen:
- Menambahkan kondisi baru untuk mengecek jika jenis aset adalah "Apartemen"
- Mengubah label `$labelNilai['tanah']` menjadi 'Luas Unit' untuk apartemen
- Menambahkan 'apartemen' ke dalam array mapping label dengan nilai 'Luas Unit'
- Memperbaiki indentasi kode untuk konsistensi format
2025-10-22 11:54:28 +07:00
Daeng Deni Mardaeni
6ed4e8ba41 🐛 fix(print): Perbaiki tampilan laporan standar & pembanding migrasi" -m "
- print-out-standar.blade.php: koreksi relasi cabang ke \$permohonan->branch->name
- print-out-standar.blade.php: koreksi tujuan penilaian ke \$permohonan->tujuanPenilaian->name
- print-out-standar.blade.php: perbaiki guard dokumen dengan @if(isset(\$dokumen))
- print-out-standar.blade.php: tambah baris 'Jenis Jaminan' khusus data migrasi (is_mig)
- print-out-standar.blade.php: sembunyikan section tertentu saat is_mig untuk konsistensi
- print-out-standar.blade.php: tampilkan Faktor Positif/Negatif/Lain-lain dari forminspeksi (dukungan array/string) dengan tabel ber-border & nl2br(e())
- print-pembanding.blade.php: render keterangan pembanding pakai {!! nl2br(e(parsePembandingMigration(...))) !!} agar line break tampil
- app/Helpers/Lpj.php: update parsePembandingMigration → pecah konten dengan tanda '#' jadi baris baru, trim, hapus baris kosong
- Konsolidasi formatting (spacing, border tabel) agar output rapi untuk kasus migrasi/non-migrasi
2025-10-21 17:24:20 +07:00
Daeng Deni Mardaeni
6b0022deed 🐛 fix(print): Hapus suffix angka dari key informasi dokumen" -m "
- print-out-sederhana.blade.php: gunakan preg_replace('/_([2-5])$/', '', $key) untuk hapus suffix angka di akhir key
- Pastikan key seperti 'nomor_2', 'tanggal_3', dst. tampil tanpa angka (jadi 'nomor', 'tanggal')
- Penyesuaian dilakukan hanya pada tampilan, tidak mempengaruhi nilai data
- Menangani data migrasi dengan struktur key numerik agar tampil lebih rapi
- Tampilan informasi dokumen jadi lebih bersih, konsisten, dan mudah dibaca
2025-10-20 15:47:10 +07:00
Daeng Deni Mardaeni
ae5a9ce5b7 🐛 fix(signature): Tangani SO/EO sama & perbaiki fallback tanggal migrasi" -m "
- signature-approval.blade.php: tambahkan kondisi SO==EO → render 1 kolom 'Diperiksa dan Menyetujui' (pakai jabatan EO)
- signature-approval.blade.php: bila SO≠EO → render kolom terpisah 'Diperiksa' (SO) & 'Menyetujui' (EO) seperti sebelumnya
- signature-approval.blade.php: jaga konsistensi label/jabatan & area tanda tangan agar tidak redundan
- PreviewLaporanService: ubah urutan fallback tanggal migrasi → tgl_laporan → tgl_oto → null
- PreviewLaporanService: parsing tanggal migrasi via Carbon::createFromFormat('d/m/Y H:i:s') → 'Y-m-d H:i:s'
- Akurasi tanggal laporan meningkat karena tgl_oto merefleksikan tanggal otorisasi/approval
- Tampilan blok signature lebih rapi pada kasus SO/EO sama; hilangkan duplikasi informasi
- Backward-compatible: perilaku SO≠EO & data non-migrasi tidak berubah
2025-10-20 10:47:49 +07:00
Daeng Deni Mardaeni
c8b5fcc9d1 🐛 fix(ui-print): Format nilai pasar wajar & tampilkan fakta migrasi
- Terapkan number_format(ID) (0 desimal, ',' desimal, '.' ribuan) untuk nilai pasar wajar Bagian 1
- print-out-sederhana: format nilai per kategori & nilai perbandingan (Bagian 1)
- print-out-standar: format nilai per kategori, nilai perbandingan (Bagian 1), dan nilai likuidasi
- Tambah section fakta khusus permohonan migrasi (is_mig) dengan tabel 'judul' per kategori
- Support konten fakta sebagai array/string; render dinamis & rapi
- Perbaiki logika tanggal laporan migrasi: fallback tgl_laporan → tgl_create → null
- Parsing tanggal migrasi via Carbon::createFromFormat('d/m/Y H:i:s') → 'Y-m-d H:i:s'
- Jaga konsistensi & keterbacaan angka besar (ribuan–miliaran) di semua laporan
2025-10-20 08:30:31 +07:00
Daeng Deni Mardaeni
2937add646 🐛 fix(ui-print): Format nilai pasar wajar & perbaiki hitung hari kerja migrasi
- Terapkan number_format(ID) untuk nilai pasar wajar (0 desimal, ',' desimal, '.' ribuan)
- print-out-sederhana: format nilai per kategori & nilai perbandingan agar konsisten
- print-out-standar: format nilai per kategori, nilai perbandingan, dan total nilai pasar wajar
- Ganti perhitungan manual floor/diffInDays menjadi fungsi hitungHariKerja() yang akurat
- Parsing tanggal migrasi pakai Carbon::createFromFormat('d/m/Y H:i:s', ...) sebelum dihitung
- Tata ulang tampilan informasi Dinas Tata Ruang: wrapper tabel 'judul', struktur tabel rapi, <br> untuk array
- Inisialisasi $totalNilaiPasarWajar = 0 dan akumulasi nilai kategori + perbandingan dengan fallback kalkulasi
- Pastikan konsistensi format angka di seluruh laporan & fallback bila data backend tidak tersedia
- Uji: verifikasi format ribuan/jutaan, akurasi hari kerja (migrasi/non-migrasi), dan total nilai terhitung benar
2025-10-16 10:04:40 +07:00
Daeng Deni Mardaeni
b83920d8aa 🐛(ui-print): Perbaiki struktur kondisional & penempatan elemen tabel
- Memperbaiki urutan kondisi @if/@else untuk penanganan data alamat pada print-out-sederhana.blade.php
- Memindahkan blok kode ke posisi tabel yang lebih logis dan konsisten
- Menghapus kode komentar dan duplikasi yang tidak digunakan
- Menyesuaikan penempatan baris 'Atas Nama' ke dalam kondisi yang sesuai
- Menata ulang elemen NIB agar berada dalam struktur tabel yang benar
- Memperbaiki indentasi dan format HTML untuk keterbacaan yang lebih baik
- Menjamin konsistensi tampilan print-out untuk berbagai kondisi data
- Mengurangi potensi error dari struktur kondisi yang tidak sinkron
- File diubah: resources/views/penilai/components/print-out-sederhana.blade.php
2025-10-15 08:31:08 +07:00
Daeng Deni Mardaeni
c6596cdea0 feat(ui-penilai): Tambah tampilan legalitas jaminan untuk data migrasi
Perubahan ini menambahkan fitur tampilan legalitas jaminan khusus untuk permohonan migrasi dan memperbaiki penanganan data informasi dinas tata ruang.

## Perubahan Utama:

### 1. Tampilan Legalitas Jaminan Migrasi (print-out-standar.blade.php)
- Menambahkan section khusus untuk menampilkan data legalitas jaminan dari migrasi
- Menggunakan data `mig_detail_legalitas_jaminan` yang di-decode dari JSON
- Menampilkan data dalam format tabel dengan label dan value yang terstruktur
- Menambahkan kondisi `@if ($permohonan->is_mig)` untuk hanya menampilkan pada permohonan migrasi

### 2. Penanganan Data Informasi Dinas Tata Ruang (informasi.blade.php)
- Memperbaiki penanganan data yang bisa berupa array atau string tunggal
- Menambahkan pengecekan `is_array($item)` sebelum melakukan loop
- Menambahkan fallback untuk menampilkan data langsung jika bukan array
- Memastikan tampilan tetap konsisten baik untuk data array maupun string

### 3. Kondisi Tampilan Form Kategori
- Menambahkan kondisi `@if(!$permohonan->is_mig)` untuk menyembunyikan form kategori tertentu pada permohonan migrasi
- Memastikan tampilan yang sesuai untuk jenis permohonan yang berbeda
2025-10-14 14:49:06 +07:00
Daeng Deni Mardaeni
c981237663 (ui-penilai): Konsolidasi tampilan migrasi, fallback user, dan label tanggal penilaian
- Menyesuaikan tampilan & logika untuk data permohonan hasil migrasi (is_mig)
- Tambahkan fallback user untuk SO, EO, dan DD agar nama tetap muncul meski data tidak lengkap
- Ubah label 'Tanggal Survei' menjadi 'Tanggal Penilaian' untuk konsistensi di seluruh halaman
- Perbaiki struktur & border tabel tanda tangan approval, tambahkan bold pada jabatan
- Sesuaikan kondisi tombol laporan agar hanya bergantung pada status_bayar
- Tambahkan logika decode mig_permohonan di print-out-sederhana untuk data migrasi
- Tambahkan fallback AO name dari data migrasi dan perhitungan hari kerja khusus is_mig
- Perbaiki struktur HTML, indentasi, dan formatting di semua view terkait
- File diubah: laporan/index, analisa/informasi, signature-approval, show, print-out-sederhana
2025-10-13 16:46:43 +07:00
Daeng Deni Mardaeni
291811b74a (service): Tambah migrasi tgl_laporan untuk permohonan is_mig & fallback dataPembanding
- Menambahkan logika penyesuaian tanggal laporan untuk permohonan hasil migrasi (is_mig)
- Gunakan mig_mst_jaminan_tgl_laporan jika is_mig = true, fallback ke lpj->created_at jika false
- Tambahkan inisialisasi dataPembanding = '' sebagai default untuk mencegah undefined variable
- Tambahkan variabel mig_permohonan untuk mempermudah akses data hasil migrasi
- Menambahkan logging decode mig_permohonan dan fallback tgl_laporan untuk debugging
- Menjaga konsistensi tanggal laporan antara data migrasi dan data baru
- Meningkatkan stabilitas render preview laporan untuk permohonan tanpa inspeksi
- File yang diubah: app/Services/PreviewLaporanService.php
- Kompatibel dengan Laravel 12, PHP 8.3, PostgreSQL
2025-10-13 16:43:30 +07:00
Daeng Deni Mardaeni
2d8bddff42 ♻️(refactor): Penamaan label, logging komprehensif, transaksi DB, komentar fungsi, dan idempotensi seeder
- Mengubah label 'Tanggal Survei' menjadi 'Tanggal Penilaian' di seluruh view Penilai & komponen cetak
- Menambahkan logging detail di setiap tahap proses (awal, validasi, eksekusi, return) pada controller, service, dan request
- Menerapkan transaksi database (begin, commit, rollback) di operasi tulis untuk konsistensi data (PostgreSQL)
- Menambahkan komentar fungsi (PHPDoc) di semua controller, service, dan model utama
- Menyederhanakan query dan filter laporan pada LaporanController dengan logging tambahan
- Menambahkan idempotensi pada semua seeder dengan updateOrCreate dan normalisasi delimiter CSV
- Meningkatkan robustness parsing timestamp multi-format dan logging batch proses seeding
- Menata ulang tampilan laporan, nilai plafond, dan penilai agar label dan UI konsisten
- Memastikan semua operasi penting tercatat di log dengan konteks user dan parameter penting
2025-10-10 23:48:15 +07:00
majid
be41a815f7 feat(pdf-viewer): implement pdf.js with pagination and swipe controls 2025-07-29 09:57:54 +07:00
majid
c459d1be70 Merge branch 'feature/senior-officer' into lpj-db-migratio 2025-07-21 10:17:51 +07:00
majid
142257d18d feat(laporan-user): refactor user report to display user data instead of permohonan
- Change data source from Permohonan to User model
- Simplify search functionality to only search by name and NIK
- Update table columns to show user information (NIK, name, roles)
- Remove date range filters and complex search conditions
- Add helper function getFilePath to handle storage paths
2025-07-21 10:13:47 +07:00
majid
32eab5f089 Merge branch 'staging' into feature/senior-officer 2025-07-14 09:03:18 +07:00
majid
84933206ee feat: add export functionality and simplify laporan user report
refactor: update laporan user service and views for simplified report
style: improve daftar pustaka views and remove unused code
fix: correct date field names in laporan sla penilai service
2025-07-11 21:16:01 +07:00
majid
1338085481 fix(activity): highlight table row in red when status is "freeze" 2025-07-11 21:09:51 +07:00
majid
3f57d361f9 feat: add export functionality and simplify laporan user report
refactor: update laporan user service and views for simplified report
style: improve daftar pustaka views and remove unused code
fix: correct date field names in laporan sla penilai service
2025-07-11 17:01:50 +07:00
majid
027c698a11 fix: perbaiki navigasi back di halaman laporan penilai dan tampilkan gambar/pdf pada daftar pustaka 2025-07-08 17:04:19 +07:00
majid
05f87f4767 frefactor(daftar-pustaka): replace manual file handling with Laravel Storage 2025-07-08 14:17:45 +07:00
majid
6bb13dbcaf fix(signature): perbaikkan tanda tangan otorisator 2025-07-08 13:39:54 +07:00
majid
02d2e42621 Merge branch 'feature/senior-officer' into lpj-db-migratio 2025-07-08 09:45:57 +07:00
majid
fd7f5fb9c2 feat(daftar-pustaka) : tambah daftar pustaka dan categori pustaka, lihat detail, tambah, edit 2025-07-07 17:27:49 +07:00
majid
dbb930557b Merge branch 'feature/senior-officer' of https://git.putrakuningan.com/daengdeni/lpj into feature/senior-officer 2025-07-07 09:50:44 +07:00
majid
7aeab00535 feat(laporan): tambah tanggal reported, kunjungan, laporan 2025-07-07 09:50:37 +07:00
majid
22e9e6b6a9 feat(print-out): tambah tanggal otorisasi di bagian tanda tangan 2025-07-07 09:44:49 +07:00
majid
e27a34d52f Merge branch 'feature/senior-officer' into lpj-db-migratio 2025-07-03 11:17:48 +07:00
majid
db3ffacebe update service preview laporan and foto jaminan 2025-06-18 10:15:49 +07:00
majid
daed7c5e48 fix: tampilan detail laporan, dan ganti route print out, penambahan service 2025-06-13 09:56:42 +07:00
majid
4405760df4 Merge branch 'staging' into lpj-db-migratio 2025-06-11 10:00:41 +07:00
majid
7ddcc14167 update migration file, dan laporan 2025-06-11 09:55:37 +07:00
majid
f69128e18e feat: penambahan pencarian berdasarkan nama debiture 2025-05-26 11:17:41 +07:00
majid
39424b5df5 fix: update tampilan penilai jaminan 2025-05-23 14:58:13 +07:00
majid
ec22e5f632 fix: update hasil laporan export dan controller 2025-05-23 14:57:15 +07:00
majid
f81cdbb50d Updates user data and improves UI components
Updates user data display across multiple views to handle migrated data.

Improves UI components by adding a detail location component and refining print layouts.
Also, it fixes minor bugs and enhances data presentation in reports and forms.
2025-05-09 14:32:48 +07:00
majid
d1ce5f2d55 fix: handle error tanggal penilaian 2025-05-05 15:47:09 +07:00
majid
2cb68b6d94 fix: perbaikkan export hasil penilai jamianan dan index permohonan 2025-05-05 13:43:35 +07:00
majid
d2b4e380b5 Merge branch 'staging' into lpj-db-migratio 2025-05-05 10:24:01 +07:00
majid
3853b5d6eb fix: perbaikkan tampilan dan print out lpj setalah migrate db old ke db new 2025-05-05 10:13:09 +07:00
majid
bf7e6275e3 feat: add migration and seeder databse lpj old to lpj new 2025-05-05 00:21:46 +07:00
179 changed files with 6397 additions and 17726 deletions

View File

@@ -32,8 +32,6 @@ class LaporanAdminKreditExport implements WithColumnFormatting, WithHeadings, Fr
$row->nilai_pasar_wajar,
$row->nilai_likuidasi,
$row->nama_penilai,
$row->kolektibilitas,
$row->keterangan,
$row->created_at
];
}
@@ -54,8 +52,6 @@ class LaporanAdminKreditExport implements WithColumnFormatting, WithHeadings, Fr
'Nilai Pasar Wajar',
'Nilai Likuidasi',
'Nama Penilai',
'Kolektibilitas',
'Keterangan',
'Created At'
];
}
@@ -68,9 +64,7 @@ class LaporanAdminKreditExport implements WithColumnFormatting, WithHeadings, Fr
'J' => NumberFormat::FORMAT_DATE_DDMMYYYY,
'K' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1,
'L' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1,
'N' => NumberFormat::FORMAT_TEXT, // Kolektibilitas
'O' => NumberFormat::FORMAT_TEXT, // Keterangan
'P' => NumberFormat::FORMAT_DATE_DATETIME, // Created At (moved from M to P)
'M' => NumberFormat::FORMAT_DATE_DATETIME,
];
}
}

View File

@@ -1,288 +1,305 @@
<?php
namespace Modules\Lpj\Exports;
namespace Modules\Lpj\Exports;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Concerns\WithCustomStartCell;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\Branch;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Concerns\WithCustomStartCell;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\Branch;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class LaporanHasilPenilaianJaminanInternalExternalExport implements FromCollection, WithHeadings, WithMapping, WithTitle, WithCustomStartCell, WithEvents
class LaporanHasilPenilaianJaminanInternalExternalExport implements FromCollection, WithHeadings, WithMapping, WithTitle, WithCustomStartCell, WithEvents
{
protected $request;
public function __construct($request)
{
protected $request;
public function __construct($request)
{
$this->request = $request;
}
public function collection()
{
$query = Permohonan::query();
$query = $query->where('status', 'done');
// Apply date range filter if provided
if ($this->request->has('start_date') || $this->request->has('end_date')) {
$query->whereBetween('tanggal_permohonan', [
$this->request->start_date ?? '1900-01-01',
$this->request->end_date ?? now()->toDateString()
]);
}
// Apply branch filter if provided
if ($this->request->has('branch_id') && !empty($this->request->branch_id)) {
$query->where('branch_id', $this->request->branch_id);
}
if ($this->request->has('penilai_id') && !empty($this->request->penilai_id)) {
$request = $this->request; // Store in a local variable
$query->whereHas('penilaian._user_penilai.userPenilaiTeam', function($q) use ($request) {
$q->where('user_id', $request->penilai_id);
});
}
// Apply search filter if provided
if ($this->request->has('search') && !empty($this->request->search)) {
$search = $this->request->search;
$query->where(function ($q) use ($search) {
$q->where('nomor_registrasi', 'LIKE', '%' . $search . '%');
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhere('status', 'LIKE', '%' . $search . '%');
});
}
// Default ordering
$query->orderBy('nomor_registrasi', 'asc');
return $query->get();
}
protected $rowNumber = 0;
public function map($permohonan): array
{
$this->rowNumber++;
$luas_tanah = 0;
$luas_bangunan = 0;
$nilai_tanah = 0;
$nilai_bangunan = 0;
$npw = 0;
$nilai_liquidasi = 0;
if (isset($permohonan->penilai->lpj)) {
$lpj = json_decode($permohonan->penilai->lpj, true);
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
$luas_tanah = $lpj['luas_tanah'] ?? 0;
$luas_bangunan = $lpj['luas_bangunan'] ?? 0;
$nilai_tanah = str_replace('.', '', $lpj['nilai_tanah_2'] ?? 0);
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
$nilai_liquidasi = str_replace('.', '', $lpj['likuidasi_nilai_2'] ?? 0);
}
return [
$this->rowNumber,
$permohonan->nomor_registrasi,
$permohonan->tanggal_permohonan,
$permohonan->debiture->branch->name,
$permohonan->creator->name,
$permohonan->debiture->cif,
$permohonan->debiture->name,
$permohonan->jenisPenilaian->name,
$permohonan->tujuanPenilaian->name,
$permohonan->jenisFasilitasKredit->name,
$permohonan->documents->pluck('jenisJaminan.name')->unique()->implode(', '),
$permohonan->documents->map(function ($document) {
return formatAlamat($document);
})->unique()->implode(', '),
$permohonan->documents->flatMap(function ($document) {
return $document->detail->map(function ($detail) {
return (!empty($detail->dokumen_nomor) && is_array($detail->dokumen_nomor))
? ($detail->jenisLegalitasJaminan->name ?? '') . "\n" . implode(', ', $detail->dokumen_nomor)
: null;
});
})->filter()->unique()->implode(', '),
$permohonan->documents->pluck('pemilik.name')->unique()->implode(', '),
$luas_tanah . ' m²',
formatRupiah($nilai_tanah, 2),
$luas_bangunan . ' m²',
formatRupiah($nilai_bangunan, 2),
formatRupiah($permohonan->nilai_njop ?? 0, 2),
formatRupiah($npw, 2),
formatRupiah($nilai_liquidasi, 2),
$permohonan->documents->map(function ($document) {
return formatTanggalIndonesia($document->created_at);
})->first(),
'', // tanggal_spk
'', // nomor_spk
'', // tanggal_rencana_kunjungan
$permohonan->penilaian->tanggal_kunjungan ? formatTanggalIndonesia($permohonan->penilaian->tanggal_kunjungan) : '',
'', // tanggal_delivered
'', // jangka_waktu_sla
($permohonan->approval_dd_at || $permohonan->approval_eo_at) ?
formatTanggalIndonesia($permohonan->approval_dd_at ?? $permohonan->approval_eo_at) : '',
$permohonan->penilaian->tanggal_kunjungan ? formatTanggalIndonesia($permohonan->penilaian->tanggal_kunjungan) : '',
$permohonan->penilaian->_user_penilai->userPenilaiTeam->name ?? '',
$permohonan->penilaian->teams ?? '',
'', // saran
'' // catatan
];
}
public function headings(): array
{
return [
'No',
'Nomor Registrasi',
'Tanggal Permohonan',
'Cabang',
'Pemohon',
'CIF',
'Nama Debitur',
'Jenis Penilaian',
'Tujuan Penilaian',
'Jenis Fasilitas Kredit',
'Jenis Agunan',
'Alamat Agunan',
'Bukti Kepemilikan',
'Nama Pemilik',
'Luas Tanah',
'Nilai Tanah',
'Luas Bangunan',
'Nilai Bangunan',
'Nilai NJOP',
'Nilai Pasar Wajar',
'Nilai Likuidasi',
'Tanggal Dokumen Diterima',
'Tanggal SPK',
'Nomor SPK',
'Tanggal Rencana Kunjungan',
'Tanggal Kunjungan',
'Tanggal Delivered',
'Jangka Waktu SLA',
'Tanggal Laporan',
'Tanggal Review',
'Nama Penilai',
'Nama Team Leader',
'Saran',
'Catatan'
];
}
/**
* @return string
*/
public function title(): string
{
return 'Laporan Hasil Penilaian Jaminan Internal & External';
}
/**
* @return string
*/
public function startCell(): string
{
return 'A7';
}
/**
* @return array
*/
public function registerEvents(): array
{
return [
AfterSheet::class => function(AfterSheet $event) {
// Get the sheet
$sheet = $event->sheet->getDelegate();
// Set the title
$sheet->setCellValue('A1', 'LAPORAN PENILAIAN JAMINAN');
$sheet->getStyle('A1')->getFont()->setBold(true)->setSize(16);
// Merge cells for title
$sheet->mergeCells('A1:AH1');
$sheet->getStyle('A1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set the branch information if filtered
$branchInfo = '';
if ($this->request->has('branch_id') && !empty($this->request->branch_id)) {
$branch = Branch::find($this->request->branch_id);
if ($branch) {
$branchInfo = 'Cabang: ' . $branch->name;
$sheet->setCellValue('A2', $branchInfo);
$sheet->mergeCells('A2:AH2');
$sheet->getStyle('A2')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
$sheet->getStyle('A2')->getFont()->setBold(true);
}
}
// Set the period
$startDate = $this->request->start_date ?? '';
$endDate = $this->request->end_date ?? '';
$rowIndex = $branchInfo ? 3 : 2;
if ($startDate && $endDate) {
$startDateFormatted = Carbon::parse($startDate)->format('d F Y');
$endDateFormatted = Carbon::parse($endDate)->format('d F Y');
$sheet->setCellValue('A' . $rowIndex, 'Periode: ' . $startDateFormatted . ' - ' . $endDateFormatted);
} else {
$sheet->setCellValue('A' . $rowIndex, 'Periode: Semua Data');
}
$sheet->mergeCells('A' . $rowIndex . ':AH' . $rowIndex);
$sheet->getStyle('A' . $rowIndex)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set the date of export
$rowIndex++;
$sheet->setCellValue('A' . $rowIndex, 'Tanggal Export: ' . Carbon::now()->format('d F Y H:i:s'));
// Set the user who exported
$rowIndex++;
$userName = Auth::user() ? Auth::user()->name : 'System';
$sheet->setCellValue('A' . $rowIndex, 'Diexport oleh: ' . $userName);
// Add a blank line
$rowIndex++;
$sheet->setCellValue('A' . $rowIndex, '');
// Style the header row
$headerRange = 'A7:' . $sheet->getHighestColumn() . '7';
$sheet->getStyle($headerRange)->getFont()->setBold(true);
$sheet->getStyle($headerRange)->getFill()
->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
->getStartColor()->setARGB('FFCCCCCC');
// Auto-size columns - fixed to handle columns beyond Z
$highestColumn = $sheet->getHighestColumn();
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
for ($i = 1; $i <= $highestColumnIndex; $i++) {
$currentColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i);
$sheet->getColumnDimension($currentColumn)->setAutoSize(true);
}
// Add borders to all cells with data
$dataRange = 'A7:' . $sheet->getHighestColumn() . $sheet->getHighestRow();
$sheet->getStyle($dataRange)->getBorders()->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
// Center align the header row
$sheet->getStyle($headerRange)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set text wrap for header cells
$sheet->getStyle($headerRange)->getAlignment()->setWrapText(true);
},
];
}
$this->request = $request;
}
public function collection()
{
$query = Permohonan::query();
$query = $query->where('status', 'done');
// Apply date range filter if provided
if ($this->request->has('start_date') || $this->request->has('end_date')) {
$startDate = $this->request->start_date ?? '1900-01-01';
$endDate = $this->request->end_date ?? now()->toDateString();
$query->where(function ($q) use ($startDate, $endDate) {
$q->whereHas('penilaian', function ($q2) use ($startDate, $endDate) {
$q2->whereBetween('tanggal_kunjungan', [$startDate, $endDate]);
});
// OR check if has penawaran with date in range
$q->orWhereHas('penawaran', function ($q3) use ($startDate, $endDate) {
$q3->whereBetween('tanggal_penilaian_sebelumnya', [$startDate, $endDate]);
});
});
}
// Apply branch filter if provided
if ($this->request->has('branch_id') && !empty($this->request->branch_id)) {
$query->where('branch_id', $this->request->branch_id);
}
if ($this->request->has('penilai_id') && !empty($this->request->penilai_id)) {
$request = $this->request; // Store in a local variable
$query->whereHas('penilaian._user_penilai.userPenilaiTeam', function ($q) use ($request) {
$q->where('user_id', $request->penilai_id);
});
}
// Apply search filter if provided
if ($this->request->has('search') && !empty($this->request->search)) {
$search = $this->request->search;
$query->where(function ($q) use ($search) {
$q->where('nomor_registrasi', 'LIKE', '%' . $search . '%');
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('debiture', DB::raw('LOWER(name)'), 'LIKE', '%' . strtolower($search) . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhere('status', 'LIKE', '%' . $search . '%');
});
}
// Default ordering
$query->orderBy('nomor_registrasi', 'asc');
return $query->get();
}
protected $rowNumber = 0;
public function map($permohonan): array
{
$this->rowNumber++;
$luas_tanah = 0;
$luas_bangunan = 0;
$nilai_tanah = 0;
$nilai_bangunan = 0;
$npw = 0;
$nilai_liquidasi = 0;
if (isset($permohonan->penilai->lpj)) {
$lpj = json_decode($permohonan->penilai->lpj, true);
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
$luas_tanah = $lpj['luas_tanah'] ?? 0;
$luas_bangunan = $lpj['luas_bangunan'] ?? 0;
$nilai_tanah = str_replace('.', '', $lpj['nilai_tanah_2'] ?? 0);
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
$nilai_liquidasi = str_replace('.', '', $lpj['likuidasi_nilai_2'] ?? 0);
}
return [
$this->rowNumber,
$permohonan->nomor_registrasi,
$permohonan->tanggal_permohonan ?? '',
$permohonan->debiture->branch->name ?? '',
$permohonan->user->name ?? $permohonan->mig_nama_ao ?? '',
$permohonan->debiture->cif ?? '',
$permohonan->debiture->name ?? '',
$permohonan->tujuanPenilaian->name,
$permohonan->jenisPenilaian->name ?? '',
$permohonan->jenisFasilitasKredit->name,
$permohonan->documents->pluck('jenisJaminan.name')->unique()->implode(', '),
$permohonan->documents->map(function ($document) {
return formatAlamat($document);
})->unique()->implode(', '),
$permohonan->documents->flatMap(function ($document) {
return $document->detail->map(function ($detail) {
return (!empty($detail->dokumen_nomor) && is_array($detail->dokumen_nomor))
? ($detail->jenisLegalitasJaminan->name ?? '') . "\n" . implode(', ', $detail->dokumen_nomor)
: null;
});
})->filter()->unique()->implode(', '),
$permohonan->documents->pluck('pemilik.name')->unique()->implode(', '),
$luas_tanah . ' m²',
formatRupiah($nilai_tanah, 2),
$luas_bangunan . ' m²',
formatRupiah($nilai_bangunan, 2),
formatRupiah($permohonan->nilai_njop ?? 0, 2),
formatRupiah($npw, 2),
formatRupiah($nilai_liquidasi, 2),
$permohonan->documents->map(function ($document) {
return formatTanggalIndonesia($document->created_at);
})->first(),
'', // tanggal_spk
'', // nomor_spk
'', // tanggal_rencana_kunjungan
$permohonan->penilaian && $permohonan->penilaian->tanggal_kunjungan
? formatTanggalIndonesia($permohonan->penilaian->tanggal_kunjungan)
: '',
'', // tanggal_delivered
'', // jangka_waktu_sla
($permohonan->approval_dd_at || $permohonan->approval_eo_at) ?
formatTanggalIndonesia($permohonan->approval_dd_at ?? $permohonan->approval_eo_at) : '',
$permohonan->penilaian && $permohonan->penilaian->tanggal_kunjungan
? formatTanggalIndonesia($permohonan->penilaian->tanggal_kunjungan)
: '',
$permohonan->penilaian->_user_penilai->userPenilaiTeam->name ?? '',
$permohonan->approveSo->name ?? '',
'', // saran
'' // catatan
];
}
public function headings(): array
{
return [
'No',
'Nomor Registrasi',
'Tanggal Permohonan',
'Cabang',
'Pemohon',
'CIF',
'Nama Debitur',
'Jenis Penilaian',
'Tujuan Penilaian',
'Jenis Fasilitas Kredit',
'Jenis Agunan',
'Alamat Agunan',
'Bukti Kepemilikan',
'Nama Pemilik',
'Luas Tanah',
'Nilai Tanah',
'Luas Bangunan',
'Nilai Bangunan',
'Nilai NJOP',
'Nilai Pasar Wajar',
'Nilai Likuidasi',
'Tanggal Dokumen Diterima',
'Tanggal SPK',
'Nomor SPK',
'Tanggal Rencana Kunjungan',
'Tanggal Kunjungan',
'Tanggal Delivered',
'Jangka Waktu SLA',
'Tanggal Laporan',
'Tanggal Review',
'Nama Penilai',
'Nama Team Leader',
'Saran',
'Catatan'
];
}
/**
* @return string
*/
public function title(): string
{
return 'Laporan Hasil Penilaian Jaminan Internal & External';
}
/**
* @return string
*/
public function startCell(): string
{
return 'A7';
}
/**
* @return array
*/
public function registerEvents(): array
{
return [
AfterSheet::class => function (AfterSheet $event) {
// Get the sheet
$sheet = $event->sheet->getDelegate();
// Set the title
$sheet->setCellValue('A1', 'LAPORAN PENILAIAN JAMINAN');
$sheet->getStyle('A1')->getFont()->setBold(true)->setSize(16);
// Merge cells for title
$sheet->mergeCells('A1:AH1');
$sheet->getStyle('A1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set the branch information if filtered
$branchInfo = '';
if ($this->request->has('branch_id') && !empty($this->request->branch_id)) {
$branch = Branch::find($this->request->branch_id);
if ($branch) {
$branchInfo = 'Cabang: ' . $branch->name;
$sheet->setCellValue('A2', $branchInfo);
$sheet->mergeCells('A2:AH2');
$sheet->getStyle('A2')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
$sheet->getStyle('A2')->getFont()->setBold(true);
}
}
// Set the period
$startDate = $this->request->start_date ?? '';
$endDate = $this->request->end_date ?? '';
$rowIndex = $branchInfo ? 3 : 2;
if ($startDate && $endDate) {
$startDateFormatted = Carbon::parse($startDate)->format('d F Y');
$endDateFormatted = Carbon::parse($endDate)->format('d F Y');
$sheet->setCellValue('A' . $rowIndex, 'Periode: ' . $startDateFormatted . ' - ' . $endDateFormatted);
} else {
$sheet->setCellValue('A' . $rowIndex, 'Periode: Semua Data');
}
$sheet->mergeCells('A' . $rowIndex . ':AH' . $rowIndex);
$sheet->getStyle('A' . $rowIndex)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set the date of export
$rowIndex++;
$sheet->setCellValue('A' . $rowIndex, 'Tanggal Export: ' . Carbon::now()->format('d F Y H:i:s'));
// Set the user who exported
$rowIndex++;
$userName = Auth::user() ? Auth::user()->name : 'System';
$sheet->setCellValue('A' . $rowIndex, 'Diexport oleh: ' . $userName);
// Add a blank line
$rowIndex++;
$sheet->setCellValue('A' . $rowIndex, '');
// Style the header row
$headerRange = 'A7:' . $sheet->getHighestColumn() . '7';
$sheet->getStyle($headerRange)->getFont()->setBold(true);
$sheet->getStyle($headerRange)->getFill()
->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
->getStartColor()->setARGB('FFCCCCCC');
// Auto-size columns - fixed to handle columns beyond Z
$highestColumn = $sheet->getHighestColumn();
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
for ($i = 1; $i <= $highestColumnIndex; $i++) {
$currentColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i);
$sheet->getColumnDimension($currentColumn)->setAutoSize(true);
}
// Add borders to all cells with data
$dataRange = 'A7:' . $sheet->getHighestColumn() . $sheet->getHighestRow();
$sheet->getStyle($dataRange)->getBorders()->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
// Center align the header row
$sheet->getStyle($headerRange)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set text wrap for header cells
$sheet->getStyle($headerRange)->getAlignment()->setWrapText(true);
},
];
}
}

View File

@@ -2,7 +2,8 @@
namespace Modules\Lpj\Exports;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
@@ -11,167 +12,299 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use Modules\Lpj\Models\Debiture;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Helpers\Lpj;
use Illuminate\Support\Facades\DB;
class LaporanPenilaiJaminanExport implements FromQuery, WithHeadings, WithMapping, ShouldAutoSize
class LaporanPenilaiJaminanExport implements FromCollection, WithHeadings, WithMapping, ShouldAutoSize
{
protected $tanggalAwal;
protected $tanggalAkhir;
protected $status;
protected $selectedIds;
public function __construct($tanggalAwal = null, $tanggalAkhir = null, $status = null, $selectedIds = null)
protected $request;
public function __construct($request)
{
$this->tanggalAwal = $tanggalAwal;
$this->tanggalAkhir = $tanggalAkhir;
$this->status = $status;
$this->selectedIds = $selectedIds;
$this->request = $request;
}
public function query()
public function collection()
{
$query = Permohonan::query()
->with(['user', 'debiture', 'branch', 'tujuanPenilaian', 'penilaian', 'dokumenjaminan.jenisJaminan','nilaiPlafond', 'penilai', 'inspeksi']);
$query = Permohonan::query();
$query = $query->where('status', 'done');
// Filter by date range if provided
if ($this->tanggalAwal && $this->tanggalAkhir) {
$query->whereBetween('tanggal_permohonan', [$this->tanggalAwal, $this->tanggalAkhir]);
}
// Apply date range filter if provided
if ($this->request->has('start_date') || $this->request->has('end_date')) {
$startDate = $this->request->start_date ?? '1900-01-01';
$endDate = $this->request->end_date ?? now()->toDateString();
$query->where('status', 'done');
// Filter by status if provided
if ($this->status) {
$types = is_array($this->status) ? $this->status : [$this->status];
$types = array_map('strtolower', $types);
$query->whereHas('penilai', function (Builder $query) use ($types) {
$query->whereIn('type_penilai', $types);
$query->where(function ($q) use ($startDate, $endDate) {
$q->whereHas('penilaian', function ($q2) use ($startDate, $endDate) {
$q2->whereBetween('tanggal_kunjungan', [$startDate, $endDate]);
});
// OR check if has penawaran with date in range
$q->orWhereHas('penawaran', function ($q3) use ($startDate, $endDate) {
$q3->whereBetween('tanggal_penilaian_sebelumnya', [$startDate, $endDate]);
});
});
}
// Filter by laporan type if provided
if ($this->request->has('laporan') && is_array($this->request->laporan) && !empty($this->request->laporan)) {
foreach ($this->request->laporan as $type) {
$query->whereHas('penilai', function ($q) use ($type) {
$q->where('type_penilai', 'LIKE', '%' . $type . '%');
});
}
}
// Apply branch filter if provided
if ($this->request->has('branch_id') && !empty($this->request->branch_id)) {
$query->where('branch_id', $this->request->branch_id);
}
// Filter by selected IDs if provided
if ($this->selectedIds) {
$selectedIds = is_array($this->selectedIds) ? $this->selectedIds : explode(',', $this->selectedIds);
if ($this->request->has('selected_ids') && !empty($this->request->selected_ids)) {
$selectedIds = is_array($this->request->selected_ids) ? $this->request->selected_ids : explode(',', $this->request->selected_ids);
$query->whereIn('id', $selectedIds);
}
return $query;
// Apply search filter if provided
if ($this->request->has('search') && !empty($this->request->search)) {
$search = $this->request->search;
$query->where(function ($q) use ($search) {
$q->where('nomor_registrasi', 'LIKE', '%' . $search . '%');
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('debiture', DB::raw('LOWER(name)'), 'LIKE', '%' . strtolower($search) . '%');
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhere('status', 'LIKE', '%' . $search . '%');
});
}
// Default ordering
$query->orderBy('nomor_registrasi', 'asc');
return $query->get();
}
public function map($row): array
protected $rowNumber = 0;
public function map($permohonan): array
{
$this->rowNumber++;
$luas_tanah = 0;
$luas_bangunan = 0;
$nilai_tanah = 0;
$nilai_bangunan = 0;
$npw = 0;
$nilai_liquidasi = 0;
// ambil data alamat dari inspeksi
$alamat_inspeksi = null;
if (isset($permohonan->penilai->lpj)) {
$lpj = json_decode($permohonan->penilai->lpj, true);
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
if ($row->inspeksi) {
$alamat_inspeksi = json_decode($row->inspeksi->data_form, true) ?? null;
$alamat_inspeksi = $alamat_inspeksi['asset']['alamat']['sesuai'] ?? $alamat_inspeksi['asset']['alamat']['tidak sesuai'] ?? [];
$luas_tanah = $lpj['luas_tanah'] ?? 0;
$luas_bangunan = $lpj['luas_bangunan'] ?? 0;
$nilai_tanah = str_replace('.', '', $lpj['nilai_tanah_2'] ?? 0);
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
$nilai_liquidasi = str_replace('.', '', $lpj['likuidasi_nilai_2'] ?? 0);
}
$alamat_inspeksi = $alamat_inspeksi['address'] ?? '-';
// ambil data dari table penilai
$fieldPenilai = ['lpj', 'resume', 'memo', 'rap', 'call-report'];
$penilaiCek = null;
// Cari field yang tersedia
foreach ($fieldPenilai as $value) {
if (!empty($row->penilai->$value)) {
$penilaiCek = $row->penilai->$value;
break;
}
}
$decodePenilai = json_decode($penilaiCek, true) ?? [];
// Ambil nilai utama
$luasTanah = $decodePenilai['luas_tanah'] ?? 0;
$nilaiTanah1 = $decodePenilai['nilai_tanah_1'] ?? 0;
$luasBangunan = $decodePenilai['luas_bangunan'] ?? 0;
$nilaiBangunan1 = $decodePenilai['nilai_bangunan_1'] ?? 0;
$totalNilaiPasar = $decodePenilai['total_nilai_pasar_wajar'] ?? 0;
$likuidasi = $decodePenilai['likuidasi'] ?? 0;
// Ambil data npw_tambahan jika ada
$npwTambahan = $decodePenilai['npw_tambahan'] ?? [];
$tambahanDetails = [];
foreach ($npwTambahan as $tambahan) {
$tambahanDetails[] = sprintf(
'%s: Luas: %s, Nilai 1: %s, Nilai 2: %s',
$tambahan['name'] ?? '-',
$tambahan['luas'] ?? 0,
$tambahan['nilai_1'] ?? 0,
$tambahan['nilai_2'] ?? 0
);
}
$tambahanSummary = implode("; ", $tambahanDetails);
// Ambil data penilaian dari table penilaian
$user_penilai = $row->penilaian->userPenilai ?? null;
$user_penilai_name = null;
foreach ($user_penilai as $value) {
if ($value->role == 'penilai') {
$user_penilai_name = $value->user->name;
$nik_penilai = $value->user->nik ?? '-';
break;
}
}
return [
$row->id,
$row->nomor_registrasi,
$row->user->name,
$row->branch->name,
$row->tujuanPenilaian->name,
$row->debiture->name,
$row->penilai->type_penilai ?? '-',
$alamat_inspeksi ?? '-',
$luasTanah,
$luasBangunan,
$nilaiTanah1,
$nilaiBangunan1,
$totalNilaiPasar,
$likuidasi,
$row->laporan->created_at ?? '-',
$user_penilai_name,
$nik_penilai,
$row->created_at,
$this->rowNumber,
$permohonan->nomor_registrasi,
$permohonan->tanggal_permohonan ?? '',
$permohonan->debiture->branch->name ?? '',
$permohonan->user->name ?? $permohonan->mig_nama_ao ?? '',
$permohonan->debiture->cif ?? '',
$permohonan->debiture->name ?? '',
$permohonan->tujuanPenilaian->name,
$permohonan->jenisPenilaian->name ?? '',
$permohonan->jenisFasilitasKredit->name,
$permohonan->documents->pluck('jenisJaminan.name')->unique()->implode(', '),
$permohonan->documents->map(function ($document) {
return formatAlamat($document);
})->unique()->implode(', '),
$permohonan->documents->flatMap(function ($document) {
return $document->detail->map(function ($detail) {
return (!empty($detail->dokumen_nomor) && is_array($detail->dokumen_nomor))
? ($detail->jenisLegalitasJaminan->name ?? '') . "\n" . implode(', ', $detail->dokumen_nomor)
: null;
});
})->filter()->unique()->implode(', '),
$permohonan->documents->pluck('pemilik.name')->unique()->implode(', '),
$luas_tanah . ' m²',
formatRupiah($nilai_tanah, 2),
$luas_bangunan . ' m²',
formatRupiah($nilai_bangunan, 2),
formatRupiah($permohonan->nilai_njop ?? 0, 2),
formatRupiah($npw, 2),
formatRupiah($nilai_liquidasi, 2),
$permohonan->documents->map(function ($document) {
return formatTanggalIndonesia($document->created_at);
})->first(),
'', // tanggal_spk
'', // nomor_spk
'', // tanggal_rencana_kunjungan
$permohonan->penilaian && $permohonan->penilaian->tanggal_kunjungan
? formatTanggalIndonesia($permohonan->penilaian->tanggal_kunjungan)
: '',
'', // tanggal_delivered
'', // jangka_waktu_sla
($permohonan->approval_dd_at || $permohonan->approval_eo_at) ?
formatTanggalIndonesia($permohonan->approval_dd_at ?? $permohonan->approval_eo_at) : '',
$permohonan->penilaian && $permohonan->penilaian->tanggal_kunjungan
? formatTanggalIndonesia($permohonan->penilaian->tanggal_kunjungan)
: '',
$permohonan->penilaian->_user_penilai->userPenilaiTeam->name ?? '',
$permohonan->approveSo->name ?? '',
$permohonan->penilai->type_penilai?? '',
];
}
public function headings(): array
{
return [
'ID',
'No',
'Nomor Registrasi',
'User Pemohon',
'Tanggal Permohonan',
'Cabang',
'Pemohon',
'CIF',
'Nama Debitur',
'Jenis Penilaian',
'Tujuan Penilaian',
'Debitur',
'Jenis Laporan',
'Lokasi Jaminan',
'Jenis Fasilitas Kredit',
'Jenis Agunan',
'Alamat Agunan',
'Bukti Kepemilikan',
'Nama Pemilik',
'Luas Tanah',
'Nilai Tanah',
'Luas Bangunan',
'Harga Tanah',
'Harga Bangunan',
'Nilai Bangunan',
'Nilai NJOP',
'Nilai Pasar Wajar',
'Likuidasi',
'Nilai Likuidasi',
'Tanggal Dokumen Diterima',
'Tanggal SPK',
'Nomor SPK',
'Tanggal Rencana Kunjungan',
'Tanggal Kunjungan',
'Tanggal Delivered',
'Jangka Waktu SLA',
'Tanggal Laporan',
'Tanggal Review',
'Nama Penilai',
'Nik Penilai',
'Created At',
'Nama Team Leader',
'Laporan',
];
}
public function columnFormats(): array
/**
* @return string
*/
public function title(): string
{
return 'Laporan Hasil Penilaian Jaminan Internal & External';
}
/**
* @return string
*/
public function startCell(): string
{
return 'A7';
}
public function registerEvents(): array
{
return [
'A' => NumberFormat::FORMAT_NUMBER,
'C' => NumberFormat::FORMAT_DATE_DATETIME,
'K' => NumberFormat::FORMAT_DATE_DATETIME,
'N' => NumberFormat::FORMAT_DATE_DATETIME
AfterSheet::class => function (AfterSheet $event) {
// Get the sheet
$sheet = $event->sheet->getDelegate();
// Set the title
$sheet->setCellValue('A1', 'LAPORAN PENILAIAN JAMINAN');
$sheet->getStyle('A1')->getFont()->setBold(true)->setSize(16);
// Merge cells for title
$sheet->mergeCells('A1:AH1');
$sheet->getStyle('A1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set the branch information if filtered
$branchInfo = '';
if ($this->request->has('branch_id') && !empty($this->request->branch_id)) {
$branch = Branch::find($this->request->branch_id);
if ($branch) {
$branchInfo = 'Cabang: ' . $branch->name;
$sheet->setCellValue('A2', $branchInfo);
$sheet->mergeCells('A2:AH2');
$sheet->getStyle('A2')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
$sheet->getStyle('A2')->getFont()->setBold(true);
}
}
// Set the period
$startDate = $this->request->start_date ?? '';
$endDate = $this->request->end_date ?? '';
$rowIndex = $branchInfo ? 3 : 2;
if ($startDate && $endDate) {
$startDateFormatted = Carbon::parse($startDate)->format('d F Y');
$endDateFormatted = Carbon::parse($endDate)->format('d F Y');
$sheet->setCellValue('A' . $rowIndex, 'Periode: ' . $startDateFormatted . ' - ' . $endDateFormatted);
} else {
$sheet->setCellValue('A' . $rowIndex, 'Periode: Semua Data');
}
$sheet->mergeCells('A' . $rowIndex . ':AH' . $rowIndex);
$sheet->getStyle('A' . $rowIndex)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set the date of export
$rowIndex++;
$sheet->setCellValue('A' . $rowIndex, 'Tanggal Export: ' . Carbon::now()->format('d F Y H:i:s'));
// Set the user who exported
$rowIndex++;
$userName = Auth::user() ? Auth::user()->name : 'System';
$sheet->setCellValue('A' . $rowIndex, 'Diexport oleh: ' . $userName);
// Add a blank line
$rowIndex++;
$sheet->setCellValue('A' . $rowIndex, '');
// Style the header row
$headerRange = 'A7:' . $sheet->getHighestColumn() . '7';
$sheet->getStyle($headerRange)->getFont()->setBold(true);
$sheet->getStyle($headerRange)->getFill()
->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
->getStartColor()->setARGB('FFCCCCCC');
// Auto-size columns - fixed to handle columns beyond Z
$highestColumn = $sheet->getHighestColumn();
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
for ($i = 1; $i <= $highestColumnIndex; $i++) {
$currentColumn = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i);
$sheet->getColumnDimension($currentColumn)->setAutoSize(true);
}
// Add borders to all cells with data
$dataRange = 'A7:' . $sheet->getHighestColumn() . $sheet->getHighestRow();
$sheet->getStyle($dataRange)->getBorders()->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
// Center align the header row
$sheet->getStyle($headerRange)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set text wrap for header cells
$sheet->getStyle($headerRange)->getAlignment()->setWrapText(true);
},
];
}
}

View File

@@ -1,245 +1,260 @@
<?php
namespace Modules\Lpj\Exports;
namespace Modules\Lpj\Exports;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Concerns\WithCustomStartCell;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\Branch;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Concerns\WithCustomStartCell;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\Branch;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class LaporanPenilaianJaminanExport implements FromCollection, WithHeadings, WithMapping, WithTitle, WithCustomStartCell, WithEvents
{
protected $request;
class LaporanPenilaianJaminanExport implements FromCollection, WithHeadings, WithMapping, WithTitle, WithCustomStartCell, WithEvents
public function __construct($request)
{
protected $request;
public function __construct($request)
{
$this->request = $request;
}
public function collection()
{
$query = Permohonan::query();
$query = $query->where('status', 'done');
// Apply date range filter if provided
if ($this->request->has('start_date') || $this->request->has('end_date')) {
$query->whereBetween('tanggal_permohonan', [
$this->request->start_date ?? '1900-01-01',
$this->request->end_date ?? now()->toDateString()
]);
}
// Apply branch filter if provided
if ($this->request->has('branch_id') && !empty($this->request->branch_id)) {
$query->where('branch_id', $this->request->branch_id);
}
if ($this->request->has('penilai_id') && !empty($this->request->penilai_id)) {
$request = $this->request; // Store in a local variable
$query->whereHas('penilaian._user_penilai.userPenilaiTeam', function($q) use ($request) {
$q->where('user_id', $request->penilai_id);
});
}
// Apply search filter if provided
if ($this->request->has('search') && !empty($this->request->search)) {
$search = $this->request->search;
$query->where(function ($q) use ($search) {
$q->where('nomor_registrasi', 'LIKE', '%' . $search . '%');
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhere('status', 'LIKE', '%' . $search . '%');
});
}
// Default ordering
$query->orderBy('nomor_registrasi', 'asc');
return $query->with(['debiture.branch'])->get();
}
protected $rowNumber = 0;
public function map($permohonan): array
{
$this->rowNumber++;
$luas_tanah = 0;
$luas_bangunan = 0;
$nilai_tanah = 0;
$nilai_bangunan = 0;
$npw = 0;
$nilai_liquidasi = 0;
if (isset($permohonan->penilai->lpj)) {
$lpj = json_decode($permohonan->penilai->lpj, true);
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
$luas_tanah = $lpj['luas_tanah'] ?? 0;
$luas_bangunan = $lpj['luas_bangunan'] ?? 0;
$nilai_tanah = str_replace('.', '', $lpj['nilai_tanah_2'] ?? 0);
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
$nilai_liquidasi = str_replace('.', '', $lpj['likuidasi_nilai_2'] ?? 0);
}
return [
$this->rowNumber,
$permohonan->nomor_registrasi,
$permohonan->tanggal_permohonan,
$permohonan->debiture->branch->name,
$permohonan->debiture->name,
$permohonan->creator->name,
$permohonan->tujuanPenilaian->name,
$permohonan->documents->pluck('jenisJaminan.name')->unique()->implode(', '),
$permohonan->documents->map(function ($document) {
return formatAlamat($document);
})->unique()->implode(', '),
$luas_tanah . ' m²',
formatRupiah($nilai_tanah, 2),
$luas_bangunan . ' m²',
formatRupiah($nilai_bangunan, 2),
($permohonan->approval_dd_at || $permohonan->approval_eo_at) ?
formatTanggalIndonesia($permohonan->approval_dd_at ?? $permohonan->approval_eo_at) : '',
$permohonan->penilaian->tanggal_kunjungan ?
formatTanggalIndonesia($permohonan->penilaian->tanggal_kunjungan) : '',
formatRupiah($npw, 2),
formatRupiah($nilai_liquidasi, 2),
$permohonan->penilaian->_user_penilai->userPenilaiTeam->name,
];
}
public function headings(): array
{
return [
'No',
'Nomor Registrasi',
'Tanggal Permohonan',
'Cabang',
'Nama Debitur',
'Pemohon',
'Tujuan Penilaian',
'Jenis Agunan',
'Alamat Agunan',
'Luas Tanah',
'Nilai Tanah',
'Luas Bangunan',
'Nilai Bangunan',
'Tanggal Laporan',
'Tanggal Review',
'Nilai Pasar Wajar',
'Nilai Likuidasi',
'Nama Penilai',
];
}
/**
* @return string
*/
public function title(): string
{
return 'Laporan Penilaian Jaminan';
}
/**
* @return string
*/
public function startCell(): string
{
return 'A7';
}
/**
* @return array
*/
public function registerEvents(): array
{
return [
AfterSheet::class => function(AfterSheet $event) {
// Get the sheet
$sheet = $event->sheet->getDelegate();
// Set the title
$sheet->setCellValue('A1', 'LAPORAN PENILAIAN JAMINAN');
$sheet->getStyle('A1')->getFont()->setBold(true)->setSize(16);
// Merge cells for title
$sheet->mergeCells('A1:R1');
$sheet->getStyle('A1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set the branch information if filtered
$branchInfo = '';
if ($this->request->has('branch_id') && !empty($this->request->branch_id)) {
$branch = Branch::find($this->request->branch_id);
if ($branch) {
$branchInfo = 'Cabang: ' . $branch->name;
$sheet->setCellValue('A2', $branchInfo);
$sheet->mergeCells('A2:R2');
$sheet->getStyle('A2')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
$sheet->getStyle('A2')->getFont()->setBold(true);
}
}
// Set the period
$startDate = $this->request->start_date ?? '';
$endDate = $this->request->end_date ?? '';
$rowIndex = $branchInfo ? 3 : 2;
if ($startDate && $endDate) {
$startDateFormatted = Carbon::parse($startDate)->format('d F Y');
$endDateFormatted = Carbon::parse($endDate)->format('d F Y');
$sheet->setCellValue('A' . $rowIndex, 'Periode: ' . $startDateFormatted . ' - ' . $endDateFormatted);
} else {
$sheet->setCellValue('A' . $rowIndex, 'Periode: Semua Data');
}
$sheet->mergeCells('A' . $rowIndex . ':R' . $rowIndex);
$sheet->getStyle('A' . $rowIndex)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set the date of export
$rowIndex++;
$sheet->setCellValue('A' . $rowIndex, 'Tanggal Export: ' . Carbon::now()->format('d F Y H:i:s'));
// Set the user who exported
$rowIndex++;
$userName = Auth::user() ? Auth::user()->name : 'System';
$sheet->setCellValue('A' . $rowIndex, 'Diexport oleh: ' . $userName);
// Add a blank line
$rowIndex++;
$sheet->setCellValue('A' . $rowIndex, '');
// Style the header row
$headerRange = 'A7:' . $sheet->getHighestColumn() . '7';
$sheet->getStyle($headerRange)->getFont()->setBold(true);
$sheet->getStyle($headerRange)->getFill()
->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
->getStartColor()->setARGB('FFCCCCCC');
// Auto-size columns
foreach(range('A', $sheet->getHighestColumn()) as $column) {
$sheet->getColumnDimension($column)->setAutoSize(true);
}
// Add borders to all cells with data
$dataRange = 'A7:' . $sheet->getHighestColumn() . $sheet->getHighestRow();
$sheet->getStyle($dataRange)->getBorders()->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
// Center align the header row
$sheet->getStyle($headerRange)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set text wrap for header cells
$sheet->getStyle($headerRange)->getAlignment()->setWrapText(true);
},
];
}
$this->request = $request;
}
public function collection()
{
$query = Permohonan::query();
$query = $query->where('status', 'done');
// Apply date range filter if provided
if ($this->request->has('start_date') || $this->request->has('end_date')) {
$startDate = $this->request->start_date ?? '1900-01-01';
$endDate = $this->request->end_date ?? now()->toDateString();
$query->where(function ($q) use ($startDate, $endDate) {
$q->whereHas('penilaian', function ($q2) use ($startDate, $endDate) {
$q2->whereBetween('tanggal_kunjungan', [$startDate, $endDate]);
});
// OR check if has penawaran with date in range
$q->orWhereHas('penawaran', function ($q3) use ($startDate, $endDate) {
$q3->whereBetween('tanggal_penilaian_sebelumnya', [$startDate, $endDate]);
});
});
}
// Apply branch filter if provided
if ($this->request->has('branch_id') && !empty($this->request->branch_id)) {
$query->where('branch_id', $this->request->branch_id);
}
if ($this->request->has('penilai_id') && !empty($this->request->penilai_id)) {
$request = $this->request; // Store in a local variable
$query->whereHas('penilaian._user_penilai.userPenilaiTeam', function ($q) use ($request) {
$q->where('user_id', $request->penilai_id);
});
}
// Apply search filter if provided
if ($this->request->has('search') && !empty($this->request->search)) {
$search = $this->request->search;
$query->where(function ($q) use ($search) {
$q->where('nomor_registrasi', 'LIKE', '%' . $search . '%');
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('debiture', DB::raw('LOWER(name)'), 'LIKE', '%' . strtolower($search) . '%');
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhere('status', 'LIKE', '%' . $search . '%');
});
}
// Default ordering
$query->orderBy('nomor_registrasi', 'asc');
dd($query->toSql(), $query->getBindings());
return $query->with(['debiture.branch'])->get();
}
protected $rowNumber = 0;
public function map($permohonan): array
{
$this->rowNumber++;
$luas_tanah = 0;
$luas_bangunan = 0;
$nilai_tanah = 0;
$nilai_bangunan = 0;
$npw = 0;
$nilai_liquidasi = 0;
if (isset($permohonan->penilai->lpj)) {
$lpj = json_decode($permohonan->penilai->lpj, true);
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
$luas_tanah = $lpj['luas_tanah'] ?? 0;
$luas_bangunan = $lpj['luas_bangunan'] ?? 0;
$nilai_tanah = str_replace('.', '', $lpj['nilai_tanah_2'] ?? 0);
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
$nilai_liquidasi = str_replace('.', '', $lpj['likuidasi_nilai_2'] ?? 0);
}
return [
$this->rowNumber,
$permohonan->nomor_registrasi,
$permohonan->tanggal_permohonan,
$permohonan->debiture->branch->name,
$permohonan->debiture->name,
$permohonan->creator->name,
$permohonan->tujuanPenilaian->name,
$permohonan->documents->pluck('jenisJaminan.name')->unique()->implode(', '),
$permohonan->documents->map(function ($document) {
return formatAlamat($document);
})->unique()->implode(', '),
$luas_tanah . ' m²',
formatRupiah($nilai_tanah, 2),
$luas_bangunan . ' m²',
formatRupiah($nilai_bangunan, 2),
($permohonan->approval_dd_at || $permohonan->approval_eo_at) ?
formatTanggalIndonesia($permohonan->approval_dd_at ?? $permohonan->approval_eo_at) : '',
$permohonan->penilaian->tanggal_kunjungan ?
formatTanggalIndonesia($permohonan->penilaian->tanggal_kunjungan) : '',
formatRupiah($npw, 2),
formatRupiah($nilai_liquidasi, 2),
$permohonan->penilaian->_user_penilai->userPenilaiTeam->name,
];
}
public function headings(): array
{
return [
'No',
'Nomor Registrasi',
'Tanggal Permohonan',
'Cabang',
'Nama Debitur',
'Pemohon',
'Tujuan Penilaian',
'Jenis Agunan',
'Alamat Agunan',
'Luas Tanah',
'Nilai Tanah',
'Luas Bangunan',
'Nilai Bangunan',
'Tanggal Laporan',
'Tanggal Review',
'Nilai Pasar Wajar',
'Nilai Likuidasi',
'Nama Penilai',
];
}
/**
* @return string
*/
public function title(): string
{
return 'Laporan Penilaian Jaminan';
}
/**
* @return string
*/
public function startCell(): string
{
return 'A7';
}
/**
* @return array
*/
public function registerEvents(): array
{
return [
AfterSheet::class => function (AfterSheet $event) {
// Get the sheet
$sheet = $event->sheet->getDelegate();
// Set the title
$sheet->setCellValue('A1', 'LAPORAN PENILAIAN JAMINAN');
$sheet->getStyle('A1')->getFont()->setBold(true)->setSize(16);
// Merge cells for title
$sheet->mergeCells('A1:R1');
$sheet->getStyle('A1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set the branch information if filtered
$branchInfo = '';
if ($this->request->has('branch_id') && !empty($this->request->branch_id)) {
$branch = Branch::find($this->request->branch_id);
if ($branch) {
$branchInfo = 'Cabang: ' . $branch->name;
$sheet->setCellValue('A2', $branchInfo);
$sheet->mergeCells('A2:R2');
$sheet->getStyle('A2')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
$sheet->getStyle('A2')->getFont()->setBold(true);
}
}
// Set the period
$startDate = $this->request->start_date ?? '';
$endDate = $this->request->end_date ?? '';
$rowIndex = $branchInfo ? 3 : 2;
if ($startDate && $endDate) {
$startDateFormatted = Carbon::parse($startDate)->format('d F Y');
$endDateFormatted = Carbon::parse($endDate)->format('d F Y');
$sheet->setCellValue('A' . $rowIndex, 'Periode: ' . $startDateFormatted . ' - ' . $endDateFormatted);
} else {
$sheet->setCellValue('A' . $rowIndex, 'Periode: Semua Data');
}
$sheet->mergeCells('A' . $rowIndex . ':R' . $rowIndex);
$sheet->getStyle('A' . $rowIndex)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set the date of export
$rowIndex++;
$sheet->setCellValue('A' . $rowIndex, 'Tanggal Export: ' . Carbon::now()->format('d F Y H:i:s'));
// Set the user who exported
$rowIndex++;
$userName = Auth::user() ? Auth::user()->name : 'System';
$sheet->setCellValue('A' . $rowIndex, 'Diexport oleh: ' . $userName);
// Add a blank line
$rowIndex++;
$sheet->setCellValue('A' . $rowIndex, '');
// Style the header row
$headerRange = 'A7:' . $sheet->getHighestColumn() . '7';
$sheet->getStyle($headerRange)->getFont()->setBold(true);
$sheet->getStyle($headerRange)->getFill()
->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
->getStartColor()->setARGB('FFCCCCCC');
// Auto-size columns
foreach (range('A', $sheet->getHighestColumn()) as $column) {
$sheet->getColumnDimension($column)->setAutoSize(true);
}
// Add borders to all cells with data
$dataRange = 'A7:' . $sheet->getHighestColumn() . $sheet->getHighestRow();
$sheet->getStyle($dataRange)->getBorders()->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
// Center align the header row
$sheet->getStyle($headerRange)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Set text wrap for header cells
$sheet->getStyle($headerRange)->getAlignment()->setWrapText(true);
},
];
}
}

View File

@@ -1,117 +0,0 @@
<?php
namespace Modules\Lpj\Exports;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class LaporanSlikExport implements FromQuery, WithHeadings, WithMapping, WithStyles
{
protected $query;
public function __construct($query)
{
$this->query = $query;
}
public function query()
{
return $this->query;
}
public function headings(): array
{
return [
'Sandi Bank',
'Kode Kantor',
'Kode Cabang',
'Tahun',
'Bulan',
'No Rekening',
'CIF',
'Nama Debitur',
'NPWP',
'No KTP',
'No Telp',
'Alamat',
'Kode Pos',
'Kode Kab/Kota',
'Kode Negara Domisili',
'Kode Jenis',
'Kode Sifat',
'Kode Valuta',
'Baki Debet',
'Kolektibilitas',
'Tanggal Mulai',
'Tanggal Jatuh Tempo',
'Tanggal Selesai',
'Tanggal Restrukturisasi',
'Kode Sebab Macet',
'Tanggal Macet',
'Kode Kondisi',
'Tanggal Kondisi',
'Nilai Agunan',
'Jenis Agunan',
'Kode Agunan',
'Peringkat Agunan',
'Fasilitas',
'Status Agunan',
'Tanggal Lapor',
'Status',
'Tanggal Dibuat',
];
}
public function map($laporanSlik): array
{
return [
$laporanSlik->sandi_bank,
$laporanSlik->kode_kantor,
$laporanSlik->kode_cabang,
$laporanSlik->tahun,
$laporanSlik->bulan,
$laporanSlik->no_rekening,
$laporanSlik->cif,
$laporanSlik->nama_debitur,
$laporanSlik->npwp,
$laporanSlik->no_ktp,
$laporanSlik->no_telp,
$laporanSlik->alamat,
$laporanSlik->kode_pos,
$laporanSlik->kode_kab_kota,
$laporanSlik->kode_negara_domisili,
$laporanSlik->kode_jenis,
$laporanSlik->kode_sifat,
$laporanSlik->kode_valuta,
$laporanSlik->baki_debet,
$laporanSlik->kolektibilitas,
$laporanSlik->tanggal_mulai,
$laporanSlik->tanggal_jatuh_tempo,
$laporanSlik->tanggal_selesai,
$laporanSlik->tanggal_restrukturisasi,
$laporanSlik->kode_sebab_macet,
$laporanSlik->tanggal_macet,
$laporanSlik->kode_kondisi,
$laporanSlik->tanggal_kondisi,
$laporanSlik->nilai_agunan,
$laporanSlik->jenis_agunan,
$laporanSlik->kode_agunan,
$laporanSlik->peringkat_agunan,
$laporanSlik->fasilitas,
$laporanSlik->status_agunan,
$laporanSlik->tanggal_lapor,
$laporanSlik->status,
$laporanSlik->created_at->format('d/m/Y H:i'),
];
}
public function styles(Worksheet $sheet)
{
return [
1 => ['font' => ['bold' => true]],
];
}
}

View File

@@ -24,7 +24,6 @@
$row->id,
$row->code,
$row->name,
$row->biaya,
$row->created_at
];
}
@@ -36,7 +35,6 @@
'ID',
'Code',
'Name',
'Biaya',
'Created At'
];
}
@@ -46,8 +44,7 @@
{
return [
'A' => NumberFormat::FORMAT_NUMBER,
'D' => NumberFormat::FORMAT_NUMBER_00,
'E' => NumberFormat::FORMAT_DATE_DATETIME
'D' => NumberFormat::FORMAT_DATE_DATETIME
];
}
}

View File

@@ -1,185 +0,0 @@
<?php
namespace Modules\Lpj\Exports;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Modules\Lpj\Models\Slik;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
/**
* Export class untuk data SLIK (Sistem Layanan Informasi Keuangan)
*
* Class ini menangani export data SLIK ke format Excel dengan:
* - Mapping data sesuai struktur SLIK
* - Format kolom yang sesuai (text, number, date)
* - Header yang informatif
*/
class SlikExport implements WithColumnFormatting, WithHeadings, FromCollection, WithMapping
{
/**
* Mengambil collection data SLIK untuk di-export
*
* @return \Illuminate\Support\Collection
*/
public function collection()
{
return Slik::orderBy('created_at', 'desc')->get();
}
/**
* Mapping data SLIK untuk setiap baris dalam Excel
*
* @param \Modules\Lpj\Models\Slik $row
* @return array
*/
public function map($row): array
{
return [
$row->id, // A - ID
$row->sandi_bank, // B - Sandi Bank
$row->tahun, // C - Tahun
$row->bulan, // D - Bulan
$row->flag_detail, // E - Flag Detail
$row->kode_register_agunan, // F - Kode Register Agunan
$row->no_rekening, // G - No Rekening
$row->cif, // H - CIF
$row->kolektibilitas, // I - Kolektibilitas
$row->fasilitas, // J - Fasilitas
$row->jenis_segmen_fasilitas, // K - Jenis Segmen Fasilitas
$row->status_agunan, // L - Status Agunan
$row->jenis_agunan, // M - Jenis Agunan
$row->peringkat_agunan, // N - Peringkat Agunan
$row->lembaga_pemeringkat, // O - Lembaga Pemeringkat
$row->jenis_pengikatan, // P - Jenis Pengikatan
$row->tanggal_pengikatan, // Q - Tanggal Pengikatan
$row->nama_pemilik_agunan, // R - Nama Pemilik Agunan
$row->bukti_kepemilikan, // S - Bukti Kepemilikan
$row->alamat_agunan, // T - Alamat Agunan
$row->lokasi_agunan, // U - Lokasi Agunan
$row->nilai_agunan, // V - Nilai Agunan
$row->nilai_agunan_menurut_ljk, // W - Nilai Agunan Menurut LJK
$row->tanggal_penilaian_ljk, // X - Tanggal Penilaian LJK
$row->nilai_agunan_penilai_independen, // Y - Nilai Agunan Penilai Independen
$row->nama_penilai_independen, // Z - Nama Penilai Independen
$row->tanggal_penilaian_penilai_independen, // AA - Tanggal Penilaian Penilai Independen
$row->jumlah_hari_tunggakan, // AB - Jumlah Hari Tunggakan
$row->status_paripasu, // AC - Status Paripasu
$row->prosentase_paripasu, // AD - Prosentase Paripasu
$row->status_kredit_join, // AE - Status Kredit Join
$row->diasuransikan, // AF - Diasuransikan
$row->keterangan, // AG - Keterangan
$row->kantor_cabang, // AH - Kantor Cabang
$row->operasi_data, // AI - Operasi Data
$row->kode_cabang, // AJ - Kode Cabang
$row->nama_debitur, // AK - Nama Debitur
$row->nama_cabang, // AL - Nama Cabang
$row->flag, // AM - Flag
$row->created_at, // AN - Created At
];
}
/**
* Header kolom untuk Excel
*
* @return array
*/
public function headings(): array
{
return [
'ID',
'Sandi Bank',
'Tahun',
'Bulan',
'Flag Detail',
'Kode Register Agunan',
'No Rekening',
'CIF',
'Kolektibilitas',
'Fasilitas',
'Jenis Segmen Fasilitas',
'Status Agunan',
'Jenis Agunan',
'Peringkat Agunan',
'Lembaga Pemeringkat',
'Jenis Pengikatan',
'Tanggal Pengikatan',
'Nama Pemilik Agunan',
'Bukti Kepemilikan',
'Alamat Agunan',
'Lokasi Agunan',
'Nilai Agunan',
'Nilai Agunan Menurut LJK',
'Tanggal Penilaian LJK',
'Nilai Agunan Penilai Independen',
'Nama Penilai Independen',
'Tanggal Penilaian Penilai Independen',
'Jumlah Hari Tunggakan',
'Status Paripasu',
'Prosentase Paripasu',
'Status Kredit Join',
'Diasuransikan',
'Keterangan',
'Kantor Cabang',
'Operasi Data',
'Kode Cabang',
'Nama Debitur',
'Nama Cabang',
'Flag',
'Created At',
];
}
/**
* Format kolom untuk Excel
*
* @return array
*/
public function columnFormats(): array
{
return [
'A' => NumberFormat::FORMAT_NUMBER, // ID
'B' => NumberFormat::FORMAT_TEXT, // Sandi Bank
'C' => NumberFormat::FORMAT_TEXT, // Tahun
'D' => NumberFormat::FORMAT_TEXT, // Bulan
'E' => NumberFormat::FORMAT_TEXT, // Flag Detail
'F' => NumberFormat::FORMAT_TEXT, // Kode Register Agunan
'G' => NumberFormat::FORMAT_TEXT, // No Rekening
'H' => NumberFormat::FORMAT_TEXT, // CIF
'I' => NumberFormat::FORMAT_TEXT, // Kolektibilitas
'J' => NumberFormat::FORMAT_TEXT, // Fasilitas
'K' => NumberFormat::FORMAT_TEXT, // Jenis Segmen Fasilitas
'L' => NumberFormat::FORMAT_TEXT, // Status Agunan
'M' => NumberFormat::FORMAT_TEXT, // Jenis Agunan
'N' => NumberFormat::FORMAT_TEXT, // Peringkat Agunan
'O' => NumberFormat::FORMAT_TEXT, // Lembaga Pemeringkat
'P' => NumberFormat::FORMAT_TEXT, // Jenis Pengikatan
'Q' => NumberFormat::FORMAT_DATE_DDMMYYYY, // Tanggal Pengikatan
'R' => NumberFormat::FORMAT_TEXT, // Nama Pemilik Agunan
'S' => NumberFormat::FORMAT_TEXT, // Bukti Kepemilikan
'T' => NumberFormat::FORMAT_TEXT, // Alamat Agunan
'U' => NumberFormat::FORMAT_TEXT, // Lokasi Agunan
'V' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1, // Nilai Agunan
'W' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1, // Nilai Agunan Menurut LJK
'X' => NumberFormat::FORMAT_DATE_DDMMYYYY, // Tanggal Penilaian LJK
'Y' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1, // Nilai Agunan Penilai Independen
'Z' => NumberFormat::FORMAT_TEXT, // Nama Penilai Independen
'AA' => NumberFormat::FORMAT_DATE_DDMMYYYY, // Tanggal Penilaian Penilai Independen
'AB' => NumberFormat::FORMAT_NUMBER, // Jumlah Hari Tunggakan
'AC' => NumberFormat::FORMAT_TEXT, // Status Paripasu
'AD' => NumberFormat::FORMAT_PERCENTAGE_00, // Prosentase Paripasu
'AE' => NumberFormat::FORMAT_TEXT, // Status Kredit Join
'AF' => NumberFormat::FORMAT_TEXT, // Diasuransikan
'AG' => NumberFormat::FORMAT_TEXT, // Keterangan
'AH' => NumberFormat::FORMAT_TEXT, // Kantor Cabang
'AI' => NumberFormat::FORMAT_TEXT, // Operasi Data
'AJ' => NumberFormat::FORMAT_TEXT, // Kode Cabang
'AK' => NumberFormat::FORMAT_TEXT, // Nama Debitur
'AL' => NumberFormat::FORMAT_TEXT, // Nama Cabang
'AM' => NumberFormat::FORMAT_TEXT, // Flag
'AN' => NumberFormat::FORMAT_DATE_DATETIME, // Created At
];
}
}

View File

@@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Modules\Location\Models\City;
use Modules\Location\Models\District;
use Modules\Location\Models\Province;
@@ -21,115 +18,29 @@
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
/**
* Format tanggal ke dalam format Bahasa Indonesia
*
* Mengubah tanggal menjadi format yang lebih mudah dibaca dalam Bahasa Indonesia.
* Contoh: "15 Januari 2024" atau "15 Januari 2024 pukul 14.30 WIB"
*
* @param string|mixed $date Tanggal yang akan diformat (string tanggal atau null)
* @param bool $time Apakah akan menampilkan waktu juga (default: false)
* @return string Tanggal yang sudah diformat dalam Bahasa Indonesia
*
* @example
* formatTanggalIndonesia('2024-01-15') // "15 Januari 2024"
* formatTanggalIndonesia('2024-01-15 14:30:00', true) // "15 Januari 2024 pukul 14.30 WIB"
* formatTanggalIndonesia(null) // ""
* formatTanggalIndonesia('invalid-date') // "invalid-date" (return as-is jika error)
*/
function formatTanggalIndonesia($date, $time = false): string
function formatTanggalIndonesia($date, $time = false)
{
Log::debug('Memulai format tanggal Indonesia', [
'date' => $date,
'time' => $time
]);
// Validasi input null atau kosong
if (empty($date)) {
Log::debug('Tanggal kosong, return empty string');
return '';
}
Carbon::setLocale('id');
try {
$waktu = Carbon::parse($date);
if (!$time) {
$result = $waktu->translatedFormat('d F Y');
Log::debug('Format tanggal berhasil', ['result' => $result]);
return $result;
return $waktu->translatedFormat('d F Y');
}
$result = $waktu->translatedFormat('d F Y') . ' pukul ' . $waktu->format('H.i') . ' WIB';
Log::debug('Format tanggal dengan waktu berhasil', ['result' => $result]);
return $result;
return $waktu->translatedFormat('d F Y') . ' pukul ' . $waktu->format('H.i') . ' WIB';
} catch (Throwable $e) {
Log::warning('Gagal parse tanggal', [
'date' => $date,
'error' => $e->getMessage()
]);
// Return input as-is jika gagal parse
return (string) $date;
return $date;
}
}
/**
* Format angka ke dalam format mata uang Rupiah Indonesia
*
* Mengubah angka menjadi format mata uang Rupiah dengan pemisah ribuan
* dan menggunakan koma sebagai pemisah desimal sesuai standar Indonesia.
*
* @param int|float|string $number Angka yang akan diformat (bisa negatif)
* @param int $decimals Jumlah digit desimal (default: 0)
* @return string Angka yang sudah diformat dalam format Rupiah
*
* @example
* formatRupiah(1500000) // "Rp 1.500.000"
* formatRupiah(1500000.50, 2) // "Rp 1.500.000,50"
* formatRupiah(-500000) // "Rp -500.000"
* formatRupiah(0) // "Rp 0"
* formatRupiah(null) // "Rp 0"
*/
function formatRupiah($number, $decimals = 0, $withSymbol = true): string
function formatRupiah($number, $decimals = 0)
{
Log::debug('Memulai format Rupiah', [
'number' => $number,
'decimals' => $decimals,
'withSymbol' => $withSymbol
]);
// Handle null atau kosong
if ($number === null || $number === '') {
Log::debug('Number null atau kosong, return Rp 0');
return $withSymbol ? 'Rp 0' : '0';
}
// Remove dots if present
$number = str_replace('.', '', (string) $number);
// Konversi ke float dan handle error
try {
$number = (float) $number;
} catch (Throwable $e) {
Log::warning('Gagal konversi number ke float', [
'number' => $number,
'error' => $e->getMessage()
]);
return $withSymbol ? 'Rp 0' : '0';
}
// Validasi decimals
$decimals = max(0, (int) $decimals);
$formatted = number_format($number, $decimals, ',', '.');
$result = $withSymbol ? 'Rp ' . $formatted : $formatted;
Log::debug('Format Rupiah berhasil', ['result' => $result]);
return $result;
$number = (float) $number;
return 'Rp ' . number_format($number, $decimals, ',', '.');
}
function formatAlamat($alamat)
{
return ($alamat->address ? $alamat->address . ', ' : '') . (isset($alamat->village) ? $alamat->village->name . ', ' : '') . (isset($alamat->city) ? $alamat->city->name . ', ' : '') . (isset($alamat->province) ? $alamat->province->name . ', ' : '') . ($alamat->village->postal_code ?? '');
@@ -187,6 +98,8 @@
$query->orWhereNull('dokumen_persetujuan');
},
)->get();
// $sql = DB::getQueryLog();
if (sizeof($query) > 0) {
$allow = false;
@@ -327,6 +240,9 @@
return $hasil;
}
// andy add
function hitungHariKerja($tanggalMulai, $tanggalSelesai)
{
$tanggalMulai = Carbon::parse($tanggalMulai)->startOfDay();
@@ -412,8 +328,7 @@
$day = str_pad(date('d'), 2, '0', STR_PAD_LEFT);
// Generate random numbers
//$randomNumber = str_pad(mt_rand(0, pow(10, $randomLength) - 1), $randomLength, '0', STR_PAD_LEFT);
$randomNumber = sprintf('%0' . $randomLength . 'd', mt_rand(0, pow(10, $randomLength) - 1));
$randomNumber = str_pad(mt_rand(0, pow(10, $randomLength) - 1), $randomLength, '0', STR_PAD_LEFT);
// Concatenate components to create the custom code
return $year . $month . $day . $randomNumber;
@@ -610,49 +525,48 @@
return '+' . $nomorBaru;
} else if (strpos($nomorHp, '0') === 0) {
$nomorBaru = substr($nomorHp, 0, 5) . "xxxxxx";
return $nomorBaru;
} else {
return "Nomor HP tidak valid";
$nomorBaru = substr($nomorHp, 0, 5) . "xxxxxx";
return $nomorBaru;
} else {
return "Nomor HP tidak valid";
}
}
function parsePembandingMigration($keterangan) {
$keterangan = preg_replace('/[-]{5,}/', '',$keterangan); // Hapus ------
$keterangan = preg_replace('/[.]{5,}/', '',$keterangan); // Hapus .....
$keterangan = preg_replace('/\s+/', ' ',$keterangan);
$keterangan = preg_replace('/\s*\n\s*/', "\n",$keterangan);
// Pecah teks per baris untuk diproses
$lines = explode("\n",$keterangan);
$cleaned = [];
foreach ($lines as $line) {
$line = trim($line);
if (!empty($line)) {
// Format angka dalam format Rp. 123.456.789
$line = preg_replace_callback('/Rp\.\s*([\d.,]+)/', function($matches) {
$angka = str_replace(['.', ','], '', $matches[1]);
return 'Rp. ' . number_format((int)$angka, 0, ',', '.');
}, $line);
// Jika ada tanda pagar (#), pisahkan menjadi baris baru
$line = str_replace('#', "\n#", $line);
$cleaned[] = $line;
}
}
function parsePembandingMigration($keterangan) {
$keterangan = preg_replace('/[-]{5,}/', '',$keterangan); // Hapus ------
$keterangan = preg_replace('/[.]{5,}/', '',$keterangan); // Hapus .....
$keterangan = preg_replace('/\s+/', ' ',$keterangan);
$keterangan = preg_replace('/\s*\n\s*/', "\n",$keterangan);
// Pecah teks per baris untuk diproses
$lines = explode("\n",$keterangan);
$cleaned = [];
foreach ($lines as $line) {
$line = trim($line);
if (!empty($line)) {
// Format angka dalam format Rp. 123.456.789
$line = preg_replace_callback('/Rp\.\s*([\d.,]+)/', function($matches) {
$angka = str_replace(['.', ','], '', $matches[1]);
return 'Rp. ' . number_format((int)$angka, 0, ',', '.');
}, $line);
// Jika ada tanda pagar (#), pisahkan menjadi baris baru
$line = str_replace('#', "\n#", $line);
$cleaned[] = $line;
}
}
return implode("\n", $cleaned);
}
/**
* get full path to internal storage file or external storage file
*
* @param string $path
* @return string
*/
return implode("\n", $cleaned);
}
/**
* get full path to internal storage file or external storage file
*
* @param string $path
* @return string
*/
function getFilePath($path)
{

View File

@@ -55,7 +55,7 @@ class ActivityController extends Controller
->when($teamId, fn ($q) => $q->where('id', $teamId));
})
->where('user_id', '!=', $user->id)
->whereHas('user.roles', fn ($q) => $q->whereIn('name', ['surveyor', 'surveyor-penilai','penilai']))
->whereHas('user.roles', fn ($q) => $q->whereIn('name', ['surveyor', 'surveyor-penilai']))
->get();
$teamId = is_array($teamId) ? $teamId : [$teamId];
@@ -336,20 +336,13 @@ class ActivityController extends Controller
$query->orderBy('nomor_registrasi', 'asc');
}
// Get total count of records before pagination
// Get the total count of records
$totalRecords = $query->count();
// Pagination
if ($request->has('page') && $request->has('size')) {
$page = (int) $request->get('page', 1);
$size = (int) $request->get('size', 10);
$offset = ($page - 1) * $size;
$query->skip($offset)->take($size);
$size = $request->get('size', 10);
if ($size == 0) {
$size = 10;
}
// Get filtered count
$filteredRecords = $query->count();
// Filter by region if user is senior-officer
if ($regionId) {
$query->whereHas('region', function ($q) use ($regionId) {
@@ -364,37 +357,36 @@ class ActivityController extends Controller
});
}
$totalRecords = $query->count();
// Pagination
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = (int) $request->get('page', 1);
$size = (int) $request->get('size', 10);
$offset = ($page - 1) * $size;
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get filtered count
// Get the filtered count of records
$filteredRecords = $query->count();
// Get data with necessary relationships
$data = $query->with(['user', 'debiture', 'branch', 'tujuanPenilaian', 'penilaian', 'dokumenjaminan','nilaiPlafond'])->get();
// Calculate total pages
$pageCount = ceil($totalRecords / $request->get('size', 10));
// Calculate the page count
$pageCount = ceil($totalRecords / $size);
// Calculate the current page number
$currentPage = max(1, $request->get('page', 1));
// Calculate total pages
$pageCount = ceil($totalRecords / $request->get('size', 10));
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $request->get('page', 1),
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]);

View File

@@ -1,183 +0,0 @@
<?php
namespace Modules\Lpj\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Exception;
use Modules\Lpj\Models\Debiture;
/**
* Controller untuk API pencarian debitur
* Digunakan untuk autocomplete search pada form pembayaran
*/
class DebiturController extends Controller
{
/**
* Pencarian debitur untuk autocomplete
*
* @param Request $request
* @return JsonResponse
*/
public function search(Request $request): JsonResponse
{
try {
// Log aktivitas pencarian
Log::info('API Debitur Search - Request', [
'query' => $request->get('q'),
'user_id' => Auth::id()
]);
$query = $request->get('q', '');
// Validasi minimal 2 karakter untuk pencarian
if (strlen($query) < 2) {
return response()->json([
'success' => false,
'message' => 'Minimal 2 karakter untuk pencarian',
'data' => []
], 400);
}
// Mulai database transaction
DB::beginTransaction();
try {
// Query pencarian debitur
// Asumsi tabel debitur dengan kolom: id, code, nama, alamat
$debiturs = Debiture::query()
->select('id', 'cif', 'name', 'address')
->whereAny(['cif','name'], 'LIKE', "%{$query}%")
->orderBy('name', 'asc')
->limit(20) // Batasi hasil maksimal 20
->get();
// Format data untuk TomSelect
$formattedData = $debiturs->map(function($debitur) {
return [
'id' => $debitur->id,
'kode_debitur' => $debitur->cif,
'name' => $debitur->name,
'address' => $debitur->address
];
});
DB::commit();
// Log hasil pencarian
Log::info('API Debitur Search - Success', [
'query' => $query,
'results_count' => $formattedData->count(),
'user_id' => Auth::id()
]);
return response()->json([
'success' => true,
'message' => 'Data debitur berhasil ditemukan',
'data' => $formattedData
]);
} catch (Exception $e) {
DB::rollback();
throw $e;
}
} catch (Exception $e) {
// Log error
Log::error('API Debitur Search - Error', [
'query' => $request->get('q'),
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'user_id' => Auth::id()
]);
return response()->json([
'success' => false,
'message' => 'Terjadi kesalahan saat mencari data debitur',
'error' => config('app.debug') ? $e->getMessage() : 'Internal Server Error'
], 500);
}
}
/**
* Get detail debitur berdasarkan code
*
* @param Request $request
* @return JsonResponse
*/
public function getByCode(Request $request): JsonResponse
{
try {
$code = $request->get('code');
if (empty($code)) {
return response()->json([
'success' => false,
'message' => 'Code debitur harus diisi',
'data' => null
], 400);
}
// Log aktivitas get detail
Log::info('API Debitur GetByCode - Request', [
'code' => $code,
'user_id' => Auth::id()
]);
DB::beginTransaction();
try {
$debitur = DB::table('debitur')
->select('id', 'code', 'nama', 'alamat', 'telepon', 'email')
->where('code', $code)
->where('status', 'aktif')
->first();
if (!$debitur) {
DB::rollback();
return response()->json([
'success' => false,
'message' => 'Debitur tidak ditemukan',
'data' => null
], 404);
}
DB::commit();
Log::info('API Debitur GetByCode - Success', [
'code' => $code,
'debitur_id' => $debitur->id,
'user_id' => Auth::id()
]);
return response()->json([
'success' => true,
'message' => 'Data debitur berhasil ditemukan',
'data' => $debitur
]);
} catch (Exception $e) {
DB::rollback();
throw $e;
}
} catch (Exception $e) {
Log::error('API Debitur GetByCode - Error', [
'code' => $request->get('code'),
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'user_id' => Auth::id()
]);
return response()->json([
'success' => false,
'message' => 'Terjadi kesalahan saat mengambil data debitur',
'error' => config('app.debug') ? $e->getMessage() : 'Internal Server Error'
], 500);
}
}
}

View File

@@ -1,209 +0,0 @@
<?php
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Lpj\Imports\BucokImport;
use Modules\Lpj\Models\Bucok;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
/**
* Controller untuk mengelola data Bucok
*
* @package Modules\Lpj\Http\Controllers
*/
class BucokController extends Controller
{
public $user;
/**
* Menampilkan halaman index bucok
*
* @return \Illuminate\View\View
*/
public function index()
{
return view('lpj::bucok.index');
}
/**
* Menampilkan detail bucok
*
* @param int $id
* @return \Illuminate\View\View
*/
public function show($id)
{
$bucok = Bucok::findOrFail($id);
return view('lpj::bucok.show', compact('bucok'));
}
/**
* Data untuk datatables dengan server-side processing
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function dataForDatatables(Request $request)
{
if (is_null($this->user) || !$this->user->can('bucok.view')) {
// abort(403, 'Sorry! You are not allowed to view bucok.');
}
// Retrieve data from the database
$query = Bucok::query();
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('nomor_tiket', 'LIKE', "%$search%")
->orWhere('deskripsi', 'LIKE', "%$search%")
->orWhere('nomor_coa', 'LIKE', "%$search%")
->orWhere('nama_coa', 'LIKE', "%$search%")
->orWhere('cost_center', 'LIKE', "%$search%")
->orWhere('nama_sub_direktorat', 'LIKE', "%$search%")
->orWhere('nama_direktorat_cabang', 'LIKE', "%$search%")
->orWhere('penyelesaian', 'LIKE', "%$search%")
->orWhere('keterangan_gantung', 'LIKE', "%$search%");
});
}
// Apply date range filter
if ($request->has('start_date') && !empty($request->get('start_date'))) {
$query->whereDate('tanggal', '>=', $request->get('start_date'));
}
if ($request->has('end_date') && !empty($request->get('end_date'))) {
$query->whereDate('tanggal', '<=', $request->get('end_date'));
}
// Apply year filter
if ($request->has('year') && !empty($request->get('year'))) {
$query->byYear($request->get('year'));
}
// Apply month filter
if ($request->has('month') && !empty($request->get('month'))) {
$query->byMonth($request->get('month'));
}
// Apply cost center filter
if ($request->has('cost_center') && !empty($request->get('cost_center'))) {
$query->byCostCenter($request->get('cost_center'));
}
// Apply completion status filter
if ($request->has('completion_status') && $request->get('completion_status') !== '') {
$isCompleted = $request->get('completion_status') == '1';
$query->byCompletionStatus($isCompleted);
}
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField', 'created_at');
$query->orderBy($column, $order);
} else {
$query->orderBy('created_at', 'desc');
}
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page with relationships
$data = $query->get();
// Transform data untuk datatables
$transformedData = $data->map(function ($item) {
return [
'id' => $item->id,
'no' => $item->no,
'tanggal' => $item->tanggal ? dateFormat($item->tanggal,true) : '-',
'bulan' => $item->bulan,
'tahun' => $item->tahun,
'nomor_tiket' => $item->nomor_tiket,
'nomor_coa' => $item->nomor_coa,
'nama_coa' => $item->nama_coa,
'deskripsi' => $item->deskripsi,
'nominal' => $item->nominal_formatted,
'penyelesaian' => $item->penyelesaian,
'umur_aging' => $item->umur_aging,
'cost_center' => $item->cost_center,
'nama_sub_direktorat' => $item->nama_sub_direktorat,
'nama_direktorat_cabang' => $item->nama_direktorat_cabang,
'tanggal_penyelesaian' => $item->tanggal_penyelesaian ? dateFormat($item->tanggal_penyelesaian,true) : '-',
'nominal_penyelesaian' => $item->nominal_penyelesaian_formatted,
'status_penyelesaian' => $item->status_penyelesaian,
'status_badge' => $item->status_badge,
'created_by' => $item->creator?->name ?? '-',
'created_at' => dateFormat($item->created_at,true)
];
});
// Calculate the page count
$pageCount = ceil($totalRecords / ($request->get('size', 10)));
// Calculate the current page number
$currentPage = $request->get('page', 1);
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $transformedData,
]);
}
/**
* Import data bucok dari Excel
*
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function import(Request $request)
{
$request->validate([
'file' => 'required|mimes:xlsx,xls,csv|max:10240' // Max 10MB
]);
DB::beginTransaction();
try {
Log::info('Importing Bucok data', ['user_id' => Auth::id(), 'filename' => $request->file('file')->getClientOriginalName()]);
$import = new BucokImport();
Excel::import($import, $request->file('file'));
$statistics = $import->getImportStatistics();
DB::commit();
Log::info('Bucok data imported successfully', ['user_id' => Auth::id()]);
return redirect()->back()->with('success', 'Data Bucok berhasil diimport. Total: ' . $statistics['total_processed'] . ', Created: ' . $statistics['created'] . ', Updated: ' . $statistics['updated'] . ', Skipped: ' . $statistics['skipped'] . ', Errors: ' . $statistics['errors']);
} catch (Exception $e) {
DB::rollback();
Log::error('Failed to import Bucok data', ['error' => $e->getMessage(), 'user_id' => Auth::id()]);
return redirect()->back()->with('error', 'Gagal import data: ' . $e->getMessage());
}
}
}

View File

@@ -129,7 +129,7 @@
// Retrieve data from the database
$query = Debiture::query();
if (!Auth::user()->hasAnyRole(['administrator','admin'])) {
if (!Auth::user()->hasAnyRole(['administrator'])) {
$query = $query->where('branch_id', Auth::user()->branch_id);
}

View File

@@ -443,7 +443,7 @@
try {
// Periksa apakah pengguna adalah admin
if (!auth()->user()->hasRole('administrator')) {
//return response()->json(['success' => false, 'message' => 'Hanya administrator yang dapat menghapus dokumen jaminan'], 403);
return response()->json(['success' => false, 'message' => 'Hanya administrator yang dapat menghapus dokumen jaminan'], 403);
}
$jaminan = DokumenJaminan::find($jaminan_id);
@@ -453,8 +453,8 @@
}
// Periksa apakah dokumen jaminan terkait dengan permohonan aktif
if ($jaminan->permohonan()->exists() && !in_array($jaminan->permohonan->status,['order','revisi'])) {
// return response()->json(['success' => false, 'message' => 'Tidak dapat menghapus dokumen jaminan yang terkait dengan permohonan aktif'], 400);
if ($jaminan->permohonan()->exists()) {
return response()->json(['success' => false, 'message' => 'Tidak dapat menghapus dokumen jaminan yang terkait dengan permohonan aktif'], 400);
}
DB::beginTransaction();

View File

@@ -3,7 +3,6 @@
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Maatwebsite\Excel\Facades\Excel;
@@ -150,8 +149,6 @@
$request->validate([
'kode_register_t24' => 'nullable',
'cif' => 'required',
'keterangan' => 'nullable|string',
'kolektibilitas' => 'nullable|string|in:1,2,3,4,5',
]);
try {
@@ -160,8 +157,6 @@
// Update only the editable fields
$laporanAdminKredit->update([
'kode_register_t24' => $request->kode_register_t24,
'keterangan' => $request->keterangan,
'kolektibilitas' => $request->kolektibilitas,
'updated_by' => Auth::id(),
]);
@@ -174,11 +169,11 @@
}
return redirect()
->route('admin-kredit.laporan.index')
->route('laporan-admin-kredit.index')
->with('success', 'Laporan Admin Kredit updated successfully');
} catch (Exception $e) {
return redirect()
->route('admin-kredit.laporan.edit', $id)
->route('laporan-admin-kredit.edit', $id)
->with('error', 'Failed to update Laporan Admin Kredit');
}
}

View File

@@ -2,10 +2,9 @@
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Modules\Lpj\Models\Permohonan;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Modules\Lpj\Http\Controllers\PenilaiController;
class LaporanController extends Controller
@@ -16,6 +15,7 @@ class LaporanController extends Controller
* Display a listing of the resource.
*/
public function __construct(PenilaiController $penilaiController){
$this->penilaiController = $penilaiController;
}
@@ -78,9 +78,6 @@ class LaporanController extends Controller
$query = Permohonan::query()
->whereIn('status',['proses-laporan','done', 'paparan', 'proses-paparan']);
if (Auth::user()->hasAnyRole(['pemohon-ao','pemohon-eo'])) {
$query = $query->where('branch_id', Auth::user()->branch_id);
}
$query = $query->orderBy('nomor_registrasi', 'desc');
// Apply search filter if provided

View File

@@ -1,213 +1,213 @@
<?php
namespace Modules\Lpj\Http\Controllers;
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Lpj\Exports\LaporanHasilPenilaianJaminanInternalExternalExport;
use Modules\Lpj\Models\Permohonan;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Lpj\Exports\LaporanHasilPenilaianJaminanInternalExternalExport;
use Modules\Lpj\Models\Permohonan;
use Illuminate\Support\Facades\DB;
class LaporanHasilPenilaianJaminanInternalExternalController extends Controller
class LaporanHasilPenilaianJaminanInternalExternalController extends Controller
{
public $user;
/**
* Display a listing of the resource.
*/
public function index()
{
public $user;
/**
* Display a listing of the resource.
*/
public function index()
{
return view('lpj::laporan_hasil_penilaian_jaminan_internal_external.index');
}
public function dataForDatatables(Request $request)
{
if (is_null($this->user) || !$this->user->can('laporan-admin-kredit.view')) {
//abort(403, 'Sorry! You are not allowed to view laporan admin kredit.');
}
// Retrieve data from the database
$query = Permohonan::query();
$query = $query->where('status', 'done');
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = json_decode($request->get('search'));
if (isset($search->start_date) || isset($search->end_date)) {
$query->whereBetween('tanggal_permohonan', [
$search->start_date ?? '1900-01-01',
$search->end_date ?? now()->toDateString()
]);
}
// Filter by branch if provided
if (isset($search->branch_id) && !empty($search->branch_id)) {
$query->where('branch_id', $search->branch_id);
}
if (isset($search->penilai_id) && !empty($search->penilai_id)) {
$query->whereHas('penilaian._user_penilai.userPenilaiTeam', function ($q) use ($search) {
$q->where('user_id', $search->penilai_id);
});
}
if (isset($search->search)) {
$query->where(function ($q) use ($search) {
$q->where('nomor_registrasi', 'LIKE', '%' . $search->search . '%');
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('debiture', DB::raw('LOWER(name)'), 'LIKE', '%' . strtolower($search->search) . '%');
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhere('status', 'LIKE', '%' . $search->search . '%');
});
}
}
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page
$data = $query->with(['debiture.branch'])->get();
$data = $data->map(function ($permohonan) {
$luas_tanah = 0;
$luas_bangunan = 0;
$nilai_tanah = 0;
$nilai_bangunan = 0;
$npw = 0;
$nilai_liquidasi = 0;
if (isset($permohonan->penilai->lpj)) {
$lpj = json_decode($permohonan->penilai->lpj, true);
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
$luas_tanah = $lpj['luas_tanah'] ?? 0;
$luas_bangunan = $lpj['luas_bangunan'] ?? 0;
// Calculate nilai_tanah dynamically by looking for all keys that start with 'nilai_tanah_'
$nilai_tanah = str_replace('.', '', $lpj['nilai_tanah_2'] ?? 0);
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
$nilai_liquidasi = str_replace('.', '', $lpj['likuidasi_nilai_2'] ?? 0);
}
return [
'id' => $permohonan->id,
'nomor_registrasi' => $permohonan->nomor_registrasi,
'jenis_penilaian' => $permohonan->jenisPenilaian?->name,
'tujuan_penilaian' => $permohonan->tujuanPenilaian?->name,
'jenis_fasilitas_kredit' => $permohonan->jenisFasilitasKredit?->name,
'branch' => $permohonan->debiture->branch?->name,
'pemohon' => $permohonan->creator?->name,
'cif' => $permohonan->debiture->cif,
'name' => $permohonan->debiture?->name,
'jenis_agunan' => $permohonan->documents?->pluck('jenisJaminan.name')
->unique()
->implode(', '),
'alamat_agunan' => $permohonan->documents?->map(function ($document) {
return formatAlamat($document);
})->unique()->implode(', '),
'bukti_kepemilikan' => (function() use ($permohonan) {
$legalitasItems = $permohonan->documents?->flatMap(function ($document) {
return $document->detail->map(function ($detail) {
// Jika tidak ada jenis legalitas jaminan, lewati
if (empty($detail->jenisLegalitasJaminan)) {
return null;
}
// Hanya tampilkan detail yang memiliki dokumen_jaminan
if (empty($detail->dokumen_jaminan)) {
return null;
}
// Tampilkan nama legalitas jaminan saja
return $detail->jenisLegalitasJaminan->name ?? '';
});
})->filter()->unique()->values()->toArray();
// Buat daftar bernomor
$result = '';
foreach ($legalitasItems as $index => $item) {
$result .= ($index + 1) . '. ' . $item . "\n";
}
return $result;
})(),
'nama_pemilik' => $permohonan->documents?->pluck('pemilik.name')
->unique()
->implode(', '),
'luas_tanah' => $luas_tanah . ' m²',
'nilai_tanah' => formatRupiah($nilai_tanah, 2),
'luas_bangunan' => $luas_bangunan . ' m²',
'nilai_bangunan' => formatRupiah($nilai_bangunan, 2),
'nilai_njop' => formatRupiah($permohonan->nilai_njop, 2),
'nilai_pasar_wajar' => formatRupiah($npw, 2),
'nilai_likuidasi' => formatRupiah($nilai_liquidasi, 2),
'tanggal_documen_diterima' => $permohonan->documents?->map(function ($document) {
return $document->created_at->format('d-m-Y');
}),
'tanggal_spk' => '',
'nomor_spk' => '',
'tanggal_rencana_kunjunagn' => '',
'tanggal_kunjungan' => '',
'taggal_delivered' => '',
'jangka_waktu_sla' => '',
'nama_penilai' => $permohonan->penilaian?->_user_penilai?->userPenilaiTeam?->name,
'nama_team_leader' => $permohonan->penilaian?->teams,
'saran' => '',
'catatan' => '',
'tanggal_permohonan' => $permohonan->tanggal_permohonan,
'tanggal_laporan' => $permohonan->approval_dd_at ?? $permohonan->approval_eo_at ?? '',
'tanggal_review' => $permohonan->penilaian?->tanggal_kunjungan ?? '',
];
});
// Calculate the page count
$pageCount = ceil($totalRecords / $request->get('size'));
// Calculate the current page number
$currentPage = $request->get('page') ?: 1;
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]);
}
public function export(Request $request)
{
return Excel::download(new LaporanHasilPenilaianJaminanInternalExternalExport($request), 'laporan_hasil_penilaian_jaminan_internal_external.xlsx');
}
return view('lpj::laporan_hasil_penilaian_jaminan_internal_external.index');
}
public function dataForDatatables(Request $request)
{
if (is_null($this->user) || !$this->user->can('laporan-admin-kredit.view')) {
//abort(403, 'Sorry! You are not allowed to view laporan admin kredit.');
}
// Retrieve data from the database
$query = Permohonan::query();
$query = $query->where('status', 'done');
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = json_decode($request->get('search'));
if (isset($search->start_date) || isset($search->end_date)) {
$query->whereBetween('tanggal_permohonan', [
$search->start_date ?? '1900-01-01',
$search->end_date ?? now()->toDateString()
]);
}
// Filter by branch if provided
if (isset($search->branch_id) && !empty($search->branch_id)) {
$query->where('branch_id', $search->branch_id);
}
if (isset($search->penilai_id) && !empty($search->penilai_id)) {
$query->whereHas('penilaian._user_penilai.userPenilaiTeam', function ($q) use ($search) {
$q->where('user_id', $search->penilai_id);
});
}
if (isset($search->search)) {
$query->where(function ($q) use ($search) {
$q->where('nomor_registrasi', 'LIKE', '%' . $search->search . '%');
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('debiture', DB::raw('LOWER(name)'), 'LIKE', '%' . strtolower($search->search) . '%');
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhere('status', 'LIKE', '%' . $search->search . '%');
});
}
}
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page
$data = $query->with(['debiture.branch'])->get();
$data = $data->map(function ($permohonan) {
$luas_tanah = 0;
$luas_bangunan = 0;
$nilai_tanah = 0;
$nilai_bangunan = 0;
$npw = 0;
$nilai_liquidasi = 0;
if (isset($permohonan->penilai->lpj)) {
$lpj = json_decode($permohonan->penilai->lpj, true);
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
$luas_tanah = $lpj['luas_tanah'] ?? 0;
$luas_bangunan = $lpj['luas_bangunan'] ?? 0;
// Calculate nilai_tanah dynamically by looking for all keys that start with 'nilai_tanah_'
$nilai_tanah = str_replace('.', '', $lpj['nilai_tanah_2'] ?? 0);
$nilai_bangunan = str_replace('.', '', $lpj['nilai_bangunan_2'] ?? 0);
$nilai_liquidasi = str_replace('.', '', $lpj['likuidasi_nilai_2'] ?? 0);
}
return [
'id' => $permohonan->id,
'nomor_registrasi' => $permohonan->nomor_registrasi,
'jenis_penilaian' => $permohonan->jenisPenilaian?->name,
'tujuan_penilaian' => $permohonan->tujuanPenilaian?->name,
'jenis_fasilitas_kredit' => $permohonan->jenisFasilitasKredit?->name,
'branch' => $permohonan->debiture->branch?->name,
'pemohon' => $permohonan->creator?->name,
'cif' => $permohonan->debiture->cif,
'name' => $permohonan->debiture?->name,
'jenis_agunan' => $permohonan->documents?->pluck('jenisJaminan.name')
->unique()
->implode(', '),
'alamat_agunan' => $permohonan->documents?->map(function ($document) {
return formatAlamat($document);
})->unique()->implode(', '),
'bukti_kepemilikan' => (function () use ($permohonan) {
$legalitasItems = $permohonan->documents?->flatMap(function ($document) {
return $document->detail->map(function ($detail) {
// Jika tidak ada jenis legalitas jaminan, lewati
if (empty($detail->jenisLegalitasJaminan)) {
return null;
}
// Hanya tampilkan detail yang memiliki dokumen_jaminan
if (empty($detail->dokumen_jaminan)) {
return null;
}
// Tampilkan nama legalitas jaminan saja
return $detail->jenisLegalitasJaminan->name ?? '';
});
})->filter()->unique()->values()->toArray();
// Buat daftar bernomor
$result = '';
foreach ($legalitasItems as $index => $item) {
$result .= ($index + 1) . '. ' . $item . "\n";
}
return $result;
})(),
'nama_pemilik' => $permohonan->documents?->pluck('pemilik.name')
->unique()
->implode(', '),
'luas_tanah' => $luas_tanah . ' m²',
'nilai_tanah' => formatRupiah($nilai_tanah, 2),
'luas_bangunan' => $luas_bangunan . ' m²',
'nilai_bangunan' => formatRupiah($nilai_bangunan, 2),
'nilai_njop' => formatRupiah($permohonan->nilai_njop, 2),
'nilai_pasar_wajar' => formatRupiah($npw, 2),
'nilai_likuidasi' => formatRupiah($nilai_liquidasi, 2),
'tanggal_documen_diterima' => $permohonan->documents?->map(function ($document) {
return $document->created_at->format('d-m-Y');
}),
'tanggal_spk' => '',
'nomor_spk' => '',
'tanggal_rencana_kunjunagn' => '',
'tanggal_kunjungan' => '',
'taggal_delivered' => '',
'jangka_waktu_sla' => '',
'nama_penilai' => $permohonan->penilaian?->_user_penilai?->userPenilaiTeam?->name,
'nama_team_leader' => $permohonan->penilaian?->teams,
'saran' => '',
'catatan' => '',
'tanggal_permohonan' => $permohonan->tanggal_permohonan,
'tanggal_laporan' => $permohonan->approval_dd_at ?? $permohonan->approval_eo_at ?? '',
'tanggal_review' => $permohonan->penilaian?->tanggal_kunjungan ?? '',
];
});
// Calculate the page count
$pageCount = ceil($totalRecords / $request->get('size'));
// Calculate the current page number
$currentPage = $request->get('page') ?: 1;
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]);
}
public function export(Request $request)
{
return Excel::download(new LaporanHasilPenilaianJaminanInternalExternalExport($request), 'laporan_hasil_penilaian_jaminan_internal_external.xlsx');
}
}

View File

@@ -32,7 +32,6 @@
// Retrieve data from the database
$query = Permohonan::query();
$query->where('status','done');
if (!Auth::user()->hasAnyRole(['administrator'])) {
$query = $query->where('branch_id', Auth::user()->branch_id);

View File

@@ -1,304 +0,0 @@
<?php
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Lpj\Models\LaporanSlik;
use Modules\Lpj\Models\Slik;
use Modules\Lpj\Exports\LaporanSlikExport;
class LaporanSlikController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('lpj::laporan-slik.index');
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): JsonResponse
{
try {
$request->validate([
'slik_id' => 'required|exists:sliks,id'
]);
$slik = Slik::findOrFail($request->slik_id);
// Cek apakah data sudah ada di laporan_slik
$existing = LaporanSlik::where('slik_id', $slik->id)->first();
if ($existing) {
return response()->json([
'success' => false,
'message' => 'Data sudah ada di laporan SLIK'
], 422);
}
// Copy data dari tabel slik ke laporan_slik
$laporanSlik = LaporanSlik::create([
'slik_id' => $slik->id,
'sandi_bank' => $slik->sandi_bank,
'kode_kantor' => $slik->kode_kantor,
'kode_cabang' => $slik->kode_cabang,
'tahun' => $slik->tahun,
'bulan' => $slik->bulan,
'no_rekening' => $slik->no_rekening,
'cif' => $slik->cif,
'kode_jenis' => $slik->kode_jenis,
'kode_jenis_ket' => $slik->kode_jenis_ket,
'kode_sifat' => $slik->kode_sifat,
'kode_sifat_ket' => $slik->kode_sifat_ket,
'kode_valuta' => $slik->kode_valuta,
'kode_valuta_ket' => $slik->kode_valuta_ket,
'baki_debet' => $slik->baki_debet,
'kolektibilitas' => $slik->kolektibilitas,
'kolektibilitas_ket' => $slik->kolektibilitas_ket,
'tanggal_mulai' => $slik->tanggal_mulai,
'tanggal_jatuh_tempo' => $slik->tanggal_jatuh_tempo,
'tanggal_selesai' => $slik->tanggal_selesai,
'tanggal_restrukturisasi' => $slik->tanggal_restrukturisasi,
'kode_sebab_macet' => $slik->kode_sebab_macet,
'kode_sebab_macet_ket' => $slik->kode_sebab_macet_ket,
'tanggal_macet' => $slik->tanggal_macet,
'kode_kondisi' => $slik->kode_kondisi,
'kode_kondisi_ket' => $slik->kode_kondisi_ket,
'tanggal_kondisi' => $slik->tanggal_kondisi,
'nilai_agunan' => $slik->nilai_agunan,
'nilai_agunan_ket' => $slik->nilai_agunan_ket,
'jenis_agunan' => $slik->jenis_agunan,
'kode_agunan' => $slik->kode_agunan,
'kode_agunan_ket' => $slik->kode_agunan_ket,
'peringkat_agunan' => $slik->peringkat_agunan,
'peringkat_agunan_ket' => $slik->peringkat_agunan_ket,
'nama_debitur' => $slik->nama_debitur,
'npwp' => $slik->npwp,
'no_ktp' => $slik->no_ktp,
'no_telp' => $slik->no_telp,
'kode_kab_kota' => $slik->kode_kab_kota,
'kode_kab_kota_ket' => $slik->kode_kab_kota_ket,
'kode_negara_domisili' => $slik->kode_negara_domisili,
'kode_negara_domisili_ket' => $slik->kode_negara_domisili_ket,
'kode_pos' => $slik->kode_pos,
'alamat' => $slik->alamat,
'fasilitas' => $slik->fasilitas,
'status_agunan' => $slik->status_agunan,
'tanggal_lapor' => $slik->tanggal_lapor,
'status' => 'active',
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
// Hapus data dari tabel slik setelah berhasil dipindahkan
$slik->delete();
return response()->json([
'success' => true,
'message' => 'Data berhasil dipindahkan ke laporan SLIK',
'data' => $laporanSlik
]);
} catch (\Exception $e) {
Log::error('Error moving SLIK to laporan: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Terjadi kesalahan saat memindahkan data'
], 500);
}
}
/**
* Data untuk datatables dengan server-side processing
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function dataForDatatables(Request $request)
{
try {
// Retrieve data from the database
$query = LaporanSlik::query();
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('sandi_bank', 'LIKE', "%$search%")
->orWhere('no_rekening', 'LIKE', "%$search%")
->orWhere('cif', 'LIKE', "%$search%")
->orWhere('nama_debitur', 'LIKE', "%$search%")
->orWhere('fasilitas', 'LIKE', "%$search%")
->orWhere('status_agunan', 'LIKE', "%$search%");
});
}
// Apply year filter
if ($request->has('year') && !empty($request->get('year'))) {
$query->where('tahun', $request->get('year'));
}
// Apply month filter
if ($request->has('month') && !empty($request->get('month'))) {
$query->where('bulan', $request->get('month'));
}
// Apply sandi bank filter
if ($request->has('sandi_bank') && !empty($request->get('sandi_bank'))) {
$query->where('sandi_bank', $request->get('sandi_bank'));
}
// Apply kolektibilitas filter
if ($request->has('kolektibilitas') && !empty($request->get('kolektibilitas'))) {
$query->where('kolektibilitas', $request->get('kolektibilitas'));
}
// Apply status filter
if ($request->has('status') && !empty($request->get('status'))) {
$query->where('status', $request->get('status'));
}
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField', 'created_at');
$query->orderBy($column, $order);
} else {
$query->orderBy('created_at', 'desc');
}
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page
$data = $query->get();
// Transform data untuk datatables
$transformedData = $data->map(function ($item) {
return [
'id' => $item->id,
'sandi_bank' => $item->sandi_bank,
'tahun' => $item->tahun,
'bulan' => $item->bulan,
'no_rekening' => $item->no_rekening,
'cif' => $item->cif,
'nama_debitur' => $item->nama_debitur,
'kolektibilitas' => $item->kolektibilitas,
'kolektibilitas_badge' => $item->kolektibilitas_badge ?? '',
'fasilitas' => $item->fasilitas,
'nilai_agunan' => $item->nilai_agunan_formatted ?? '',
'status_agunan' => $item->status_agunan,
'status_badge' => $item->status_badge ?? '',
'created_by' => $item->creator?->name ?? '-',
'created_at' => dateFormat($item->created_at, true) ?? $item->created_at->format('d/m/Y H:i')
];
});
// Calculate the page count
$pageCount = ceil($totalRecords / ($request->get('size', 10)));
// Calculate the current page number
$currentPage = $request->get('page', 1);
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $transformedData,
]);
} catch (\Exception $e) {
Log::error('Error in laporan slik datatables: ' . $e->getMessage());
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => 0,
'recordsFiltered' => 0,
'pageCount' => 0,
'page' => 1,
'totalCount' => 0,
'data' => [],
]);
}
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\View\View
*/
public function show($id)
{
try {
$laporanSlik = LaporanSlik::findOrFail($id);
return view('lpj::laporan-slik.show', compact('laporanSlik'));
} catch (\Exception $e) {
Log::error('Error showing laporan slik: ' . $e->getMessage());
return back()->with('error', 'Data tidak ditemukan');
}
}
/**
* Export laporan SLIK to Excel
*/
public function export(Request $request)
{
try {
$query = LaporanSlik::query();
// Apply filters
if ($request->has('search') && $request->search) {
$search = $request->search;
$query->where(function($q) use ($search) {
$q->where('nama_debitur', 'like', "%{$search}%")
->orWhere('no_rekening', 'like', "%{$search}%")
->orWhere('cif', 'like', "%{$search}%");
});
}
if ($request->has('year') && $request->year) {
$query->where('tahun', $request->year);
}
if ($request->has('month') && $request->month) {
$query->where('bulan', $request->month);
}
if ($request->has('status') && $request->status) {
$query->where('status', $request->status);
}
$filename = 'laporan-slik-' . now()->format('Y-m-d-His') . '.xlsx';
return Excel::download(new LaporanSlikExport($query), $filename);
} catch (\Exception $e) {
Log::error('Error exporting laporan slik: ' . $e->getMessage());
return back()->with('error', 'Gagal export data: ' . $e->getMessage());
}
}
}

View File

@@ -1,558 +0,0 @@
<?php
namespace Modules\Lpj\Http\Controllers;
use Exception;
use Modules\Lpj\Models\Noc;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\Lpj\Models\Permohonan;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Support\Facades\Storage;
class MemoController extends Controller
{
public $user;
/**
* Menampilkan halaman index memo penyelesaian
*
* @return \Illuminate\View\View
*/
public function index()
{
Log::info('MemoController: Mengakses halaman index memo penyelesaian');
return view('lpj::memo.index');
}
/**
* Menampilkan form untuk membuat memo penyelesaian dengan data yang dipilih
*
* @param Request $request
* @return \Illuminate\View\View
*/
public function create(Request $request)
{
Log::info('MemoController: Mengakses halaman create memo penyelesaian');
$selectedIds = $request->get('selected_ids', []);
// Pastikan $selectedIds selalu berupa array
if (is_string($selectedIds)) {
$selectedIds = explode(',', $selectedIds);
}
// Filter array untuk menghilangkan nilai kosong
$selectedIds = array_filter($selectedIds, function($id) {
return !empty(trim($id));
});
$permohonanList = [];
$totalBiayaPJ = 0;
if (!empty($selectedIds) && count($selectedIds) > 0) {
try {
$permohonanList = Permohonan::with([
'user',
'debiture',
'branch',
'tujuanPenilaian',
'penilaian',
'jenisFasilitasKredit',
'documents.inspeksi',
'penilai',
'documents.detail',
'noc'
])->whereIn('id', $selectedIds)->get();
// Hitung total biaya PJ dari nominal_bayar di tabel NOC
$totalBiayaPJ = Noc::whereIn('permohonan_id', $selectedIds)
->sum('nominal_bayar');
Log::info('MemoController: Total Biaya PJ dihitung: ' . $totalBiayaPJ);
} catch (Exception $e) {
Log::error('MemoController: Error saat mengambil data permohonan - ' . $e->getMessage());
return redirect()->back()->with('error', 'Terjadi kesalahan saat memuat data');
}
}
return view('lpj::memo.create', compact('permohonanList', 'totalBiayaPJ'));
}
/**
* Menyimpan memo penyelesaian yang telah dibuat
*
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
Log::info('MemoController: Memulai proses penyimpanan memo penyelesaian');
DB::beginTransaction();
try {
// Validasi input
$request->validate([
'permohonan_ids' => 'required|array',
'permohonan_ids.*' => 'exists:permohonan,id',
'memo_number' => 'required|string|max:255',
'payment_date' => 'required|date',
'memo_date' => 'required|date'
]);
$permohonanIds = $request->permohonan_ids;
$memoNumber = $request->memo_number;
$paymentDate = $request->payment_date;
$memoDate = $request->memo_date;
// Update status permohonan yang dipilih
foreach ($permohonanIds as $permohonanId) {
$permohonan = Permohonan::find($permohonanId);
if ($permohonan) {
$permohonan->status = 'memo-penyelesaian';
$permohonan->memo_penyelesaian_number = $memoNumber;
$permohonan->memo_penyelesaian_date = $memoDate;
$permohonan->memo_penyelesaian_payment_date = $paymentDate;
$permohonan->memo_penyelesaian_created_at = now();
//$permohonan->save();
Log::info('MemoController: Berhasil update permohonan ID: ' . $permohonanId);
}
}
DB::commit();
Log::info('MemoController: Berhasil menyimpan memo penyelesaian untuk ' . count($permohonanIds) . ' permohonan');
return redirect()->route('memo.index')
->with('success', 'Memo penyelesaian berhasil dibuat untuk ' . count($permohonanIds) . ' permohonan');
} catch (Exception $e) {
DB::rollback();
Log::error('MemoController: Error saat menyimpan memo penyelesaian - ' . $e->getMessage());
return redirect()->back()
->withInput()
->with('error', 'Terjadi kesalahan saat menyimpan memo penyelesaian: ' . $e->getMessage());
}
}
/**
* Menampilkan detail memo penyelesaian
*
* @param int $id
* @return \Illuminate\View\View
*/
public function show($id)
{
Log::info('MemoController: Mengakses detail memo penyelesaian ID: ' . $id);
$permohonan = Permohonan::with([
'user',
'debiture',
'branch',
'tujuanPenilaian',
'penilaian',
'jenisFasilitasKredit',
'documents.inspeksi',
'penilai',
'documents.detail',
'noc'
])->findOrFail($id);
return view('lpj::memo.show', compact('permohonan'));
}
/**
* Mengambil data untuk datatables pada halaman memo penyelesaian
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function dataForDatatables(Request $request)
{
Log::info('MemoController: Mengambil data untuk datatables');
if (is_null($this->user) || !$this->user->can('debitur.view')) {
Log::warning('MemoController: User tidak memiliki permission untuk melihat data');
// abort(403, 'Sorry! You are not allowed to view users.');
}
// Mengambil data dari database dengan kondisi yang sama seperti LaporanController
$query = Permohonan::query()
->whereIn('status', ['proses-laporan', 'done', 'paparan', 'proses-paparan', 'memo-penyelesaian'])
/*->whereNotNull('approval_so_at')
->whereNotNull('approval_eo_at')
->where(function ($q) {
$q->whereIn('nilai_plafond_id', [1, 4])
->whereNotNull('approval_dd_at')
->orWhereIn('nilai_plafond_id', [2, 3]);
})*/
->whereHas('noc'); // Hanya tampilkan permohonan yang memiliki NOC
$query = $query->orderBy('nomor_registrasi', 'desc');
// Apply search filter jika ada
if ($request->has('search') && !empty($request->get('search'))) {
$searchParams = explode('|', $request->get('search'));
$filterJenisPenilaian = $searchParams[0] ?? '';
$searchTerm = $searchParams[1] ?? '';
// Filter berdasarkan jenis penilaian
if (!empty($filterJenisPenilaian)) {
$query->where('jenis_penilaian_id', $filterJenisPenilaian);
Log::info('Applied jenis penilaian filter', ['filter' => $filterJenisPenilaian]);
}
$query->where(function ($q) use ($searchTerm) {
$q->where('nomor_registrasi', 'LIKE', '%' . $searchTerm . '%');
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $searchTerm . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $searchTerm . '%');
$q->orWhereRelation('debiture', 'name', 'LIKE', '%' . $searchTerm . '%');
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $searchTerm . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $searchTerm . '%');
$q->orWhere('status', 'LIKE', '%' . $searchTerm . '%');
});
}
// Apply sorting jika ada
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Mendapatkan total count records
$totalRecords = $query->count();
$size = $request->get('size', 10);
if ($size == 0) {
$size = 10;
}
// Apply pagination jika ada
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size;
$query->skip($offset)->take($size);
}
// Mendapatkan filtered count records
$filteredRecords = $query->count();
// Mendapatkan data untuk halaman saat ini
$data = $query->with([
'user',
'debiture',
'branch',
'tujuanPenilaian',
'jenisPenilaian',
'penilaian',
'jenisFasilitasKredit',
'documents.inspeksi',
'penilai',
'documents.detail',
'noc'
])->get();
// Menghitung page count
$pageCount = ceil($totalRecords / $size);
// Menghitung current page number
$currentPage = max(1, $request->get('page', 1));
Log::info('MemoController: Berhasil mengambil data datatables - Total: ' . $totalRecords . ', Filtered: ' . $filteredRecords);
// Return response data sebagai JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]);
}
/**
* Mengambil total biaya PJ berdasarkan permohonan yang dipilih
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function getTotalBiayaPJ(Request $request)
{
Log::info('MemoController: Mengambil total biaya PJ');
try {
$permohonanIds = $request->get('permohonan_ids', []);
// Pastikan $permohonanIds selalu berupa array
if (is_string($permohonanIds)) {
$permohonanIds = explode(',', $permohonanIds);
}
// Filter array untuk menghilangkan nilai kosong
$permohonanIds = array_filter($permohonanIds, function($id) {
return !empty(trim($id));
});
$totalBiayaPJ = 0;
if (!empty($permohonanIds) && count($permohonanIds) > 0) {
// Hitung total biaya PJ dari nominal_bayar di tabel NOC
$totalBiayaPJ = \Modules\Lpj\Models\Noc::whereIn('permohonan_id', $permohonanIds)
->sum('nominal_bayar');
}
Log::info('MemoController: Total Biaya PJ berhasil dihitung: ' . $totalBiayaPJ);
return response()->json([
'success' => true,
'total_biaya_pj' => $totalBiayaPJ,
'total_biaya_pj_formatted' => 'Rp ' . number_format($totalBiayaPJ, 0, ',', '.')
]);
} catch (Exception $e) {
Log::error('MemoController: Error saat menghitung total biaya PJ - ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Terjadi kesalahan saat menghitung total biaya PJ'
], 500);
}
}
/**
* Menampilkan preview memo penyelesaian sebelum menyimpan
*
* @param Request $request
* @return \Illuminate\View\View
*/
public function preview(Request $request)
{
Log::info('MemoController: Mengakses halaman preview memo penyelesaian');
$permohonanIds = $request->permohonan_ids;
$memoNumber = $request->memo_number;
$paymentDate = $request->payment_date;
$memoDate = $request->memo_date;
try {
// Ambil data permohonan yang dipilih
$permohonanList = Permohonan::with([
'user',
'debiture',
'branch',
'penilaian',
'jenisPenilaian',
'jenisFasilitasKredit',
'documents.inspeksi',
'penilai',
'documents.detail',
'noc'
])->whereIn('id', $permohonanIds);
// Hitung total biaya PJ dari nominal_bayar di tabel NOC
$totalBiayaPJ = Noc::whereIn('permohonan_id', $permohonanIds)
->sum('nominal_bayar');
// Data untuk template memo
$memoData = [
'memo_number' => $memoNumber,
'memo_date' => $memoDate,
'payment_date' => $paymentDate,
'total_biaya_pj' => $totalBiayaPJ,
'permohonan_list' => $permohonanList->get(),
'debitur_count' => $permohonanList->get()->count(),
'jaminan_info' => $this->getJaminanInfo($permohonanList->get()),
'jenisPenilaian' => $permohonanList->pluck('jenis_penilaian_id')->first()
];
$permohonanList= $permohonanList->get();
Log::info('MemoController: Data preview memo berhasil disiapkan');
return view('lpj::memo.preview', compact('memoData', 'permohonanList', 'totalBiayaPJ'));
} catch (Exception $e) {
Log::error('MemoController: Error saat menyiapkan preview memo - ' . $e->getMessage());
return redirect()->back()
->withInput()
->with('error', 'Terjadi kesalahan saat menyiapkan preview memo: ' . $e->getMessage());
}
}
/**
* Generate PDF memo penyelesaian dan simpan ke database
*
* @param Request $request
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function generatePdf(Request $request)
{
Log::info('MemoController: Memulai generate PDF memo penyelesaian');
DB::beginTransaction();
try {
// Validasi input
$permohonanIds = $request->permohonan_ids;
$memoNumber = $request->memo_number;
$paymentDate = $request->payment_date;
$memoDate = $request->memo_date;
// Ambil data permohonan yang dipilih
$permohonanList = Permohonan::with([
'user',
'debiture',
'branch',
'penilaian',
'jenisPenilaian',
'jenisFasilitasKredit',
'documents.inspeksi',
'penilai',
'documents.detail',
'noc'
])->whereIn('id', $permohonanIds);
// Hitung total biaya PJ dari nominal_bayar di tabel NOC
$totalBiayaPJ = Noc::whereIn('permohonan_id', $permohonanIds)
->sum('nominal_bayar');
// Data untuk template memo
$memoData = [
'memo_number' => $memoNumber,
'memo_date' => $memoDate,
'payment_date' => $paymentDate,
'total_biaya_pj' => $totalBiayaPJ,
'permohonan_list' => $permohonanList->get(),
'debitur_count' => $permohonanList->get()->count(),
'jaminan_info' => $this->getJaminanInfo($permohonanList->get()),
'jenisPenilaian' => $permohonanList->pluck('jenis_penilaian_id')->first()
];
$permohonanList= $permohonanList->get();
// Generate PDF dari template
$pdf = Pdf::loadView('lpj::memo.pdf-template', compact('memoData', 'permohonanList', 'totalBiayaPJ'))
->setPaper('a4', 'portrait')
->setOptions([
'defaultFont' => 'Times-Roman',
'isRemoteEnabled' => true,
'isHtml5ParserEnabled' => true,
'isPhpEnabled' => true,
'dpi' => 150,
'defaultPaperSize' => 'a4',
'chroot' => public_path(),
]);
// Nama file PDF
$fileName = 'memo-penyelesaian-' . str_replace(['/', ' '], ['-', '-'], $memoNumber) . '-' . date('Y-m-d-H-i-s') . '.pdf';
$filePath = 'memo-penyelesaian/' . $fileName;
// Simpan PDF ke storage
Storage::disk('public')->put($filePath, $pdf->output());
// Update status permohonan yang dipilih
// Update data di tabel NOC untuk setiap permohonan
foreach ($permohonanIds as $permohonanId) {
// Cari NOC berdasarkan permohonan_id
$noc = Noc::where('permohonan_id', $permohonanId)->first();
if ($noc) {
// Update field memo penyelesaian di tabel NOC
$noc->memo_penyelesaian = $filePath;
$noc->memo_penyelesaian_number = $memoNumber;
$noc->memo_penyelesaian_date = $memoDate;
$noc->memo_penyelesaian_payment_date = $paymentDate;
$noc->memo_penyelesaian_created_at = now();
$noc->save();
Log::info('MemoController: Berhasil update NOC untuk permohonan ID: ' . $permohonanId);
} else {
Log::warning('MemoController: NOC tidak ditemukan untuk permohonan ID: ' . $permohonanId);
}
}
DB::commit();
Log::info('MemoController: Berhasil generate PDF dan menyimpan memo penyelesaian untuk ' . count($permohonanIds) . ' permohonan');
// Return PDF untuk download
return $pdf->download('memo-penyelesaian-' . $memoNumber . '.pdf');
} catch (Exception $e) {
DB::rollback();
Log::error('MemoController: Error saat generate PDF memo penyelesaian - ' . $e->getMessage());
return redirect()->back()
->with('error', 'Terjadi kesalahan saat generate PDF memo penyelesaian: ' . $e->getMessage())
->withInput();
}
}
/**
* Helper function untuk mendapatkan informasi jaminan
*
* @param $permohonanList
* @return string
*/
private function getJaminanInfo($permohonanList)
{
$jaminanTypes = [];
foreach ($permohonanList as $permohonan) {
if ($permohonan->tujuanPenilaian) {
$jaminanTypes[] = $permohonan->tujuanPenilaian->name;
}
}
$uniqueJaminan = array_unique($jaminanTypes);
return implode(' & ', $uniqueJaminan);
}
/**
* Download PDF memo penyelesaian
*
* @param int $id - ID permohonan
* @return \Illuminate\Http\Response
*/
public function downloadPdf($id)
{
Log::info('MemoController: Download PDF memo penyelesaian untuk permohonan ID: ' . $id);
try {
// Cari NOC berdasarkan permohonan_id
$noc = Noc::where('permohonan_id', $id)->first();
if (!$noc || !$noc->memo_penyelesaian) {
Log::warning('MemoController: PDF memo penyelesaian tidak ditemukan untuk permohonan ID: ' . $id);
return redirect()->back()->with('error', 'File PDF memo penyelesaian tidak ditemukan.');
}
// Cek apakah file ada di storage
if (!Storage::disk('public')->exists($noc->memo_penyelesaian)) {
Log::warning('MemoController: File PDF tidak ada di storage: ' . $noc->memo_penyelesaian);
return redirect()->back()->with('error', 'File PDF tidak ditemukan di server.');
}
// Download file
$fileName = 'memo-penyelesaian-' . $noc->memo_penyelesaian_number . '.pdf';
Log::info('MemoController: Berhasil download PDF memo penyelesaian: ' . $fileName);
return Storage::disk('public')->download($noc->memo_penyelesaian, $fileName);
} catch (Exception $e) {
Log::error('MemoController: Error saat download PDF memo penyelesaian - ' . $e->getMessage());
return redirect()->back()->with('error', 'Terjadi kesalahan saat mengunduh file PDF.');
}
}
}

View File

@@ -5,8 +5,6 @@
use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Lpj\Exports\NilaiPlafondExport;
use Modules\Lpj\Http\Requests\NilaiPlafondRequest;
@@ -16,239 +14,137 @@
{
public $user;
/**
* Menampilkan halaman daftar Nilai Plafond.
* Log setiap akses dan sebelum return view.
*/
public function index()
{
Log::info('NilaiPlafondController@index: akses halaman index');
return \view('lpj::nilai_plafond.index');
return view('lpj::nilai_plafond.index');
}
/**
* Menyimpan data Nilai Plafond baru termasuk field biaya.
* Gunakan validasi dari NilaiPlafondRequest, log proses, dan bungkus dengan transaksi DB.
*/
public function store(NilaiPlafondRequest $request)
{
Log::info('NilaiPlafondController@store: mulai proses simpan');
$validate = $request->validated();
if ($validate) {
DB::beginTransaction();
try {
$record = NilaiPlafond::create($validate);
DB::commit();
Log::info('NilaiPlafondController@store: simpan berhasil', ['id' => $record->id]);
return \redirect()
// Save to database
NilaiPlafond::create($validate);
return redirect()
->route('basicdata.nilai-plafond.index')
->with('success', 'Nilai Plafond berhasil dibuat');
->with('success', 'Jenis Aset created successfully');
} catch (Exception $e) {
DB::rollBack();
Log::error('NilaiPlafondController@store: simpan gagal', ['error' => $e->getMessage()]);
return \redirect()
return redirect()
->route('basicdata.nilai-plafond.create')
->with('error', 'Gagal membuat Nilai Plafond');
->with('error', 'Failed to create nilai plafond');
}
}
}
/**
* Menampilkan form pembuatan Nilai Plafond.
* Log akses sebelum return view.
*/
public function create()
{
Log::info('NilaiPlafondController@create: akses halaman create');
return \view('lpj::nilai_plafond.create');
return view('lpj::nilai_plafond.create');
}
/**
* Menampilkan form edit Nilai Plafond berdasarkan ID.
* Gunakan transaksi untuk pembacaan data dan logging.
*/
public function edit($id)
{
Log::info('NilaiPlafondController@edit: mulai proses edit', ['id' => $id]);
DB::beginTransaction();
try {
$nilaiPlafond = NilaiPlafond::find($id);
DB::commit();
Log::info('NilaiPlafondController@edit: data ditemukan', ['id' => $id]);
return \view('lpj::nilai_plafond.create', compact('nilaiPlafond'));
} catch (Exception $e) {
DB::rollBack();
Log::error('NilaiPlafondController@edit: gagal mengambil data', ['id' => $id, 'error' => $e->getMessage()]);
return \redirect()
->route('basicdata.nilai-plafond.index')
->with('error', 'Gagal mengambil data Nilai Plafond');
}
$nilaiPlafond = NilaiPlafond::find($id);
return view('lpj::nilai_plafond.create', compact('nilaiPlafond'));
}
/**
* Memperbarui data Nilai Plafond termasuk field biaya.
* Validasi input, logging, dan gunakan transaksi DB.
*/
public function update(NilaiPlafondRequest $request, $id)
{
Log::info('NilaiPlafondController@update: mulai proses update', ['id' => $id]);
$validate = $request->validated();
if ($validate) {
DB::beginTransaction();
try {
// Update in database
$nilaiPlafond = NilaiPlafond::find($id);
if (!$nilaiPlafond) {
Log::warning('NilaiPlafondController@update: data tidak ditemukan', ['id' => $id]);
DB::rollBack();
return \redirect()
->route('basicdata.nilai-plafond.index')
->with('error', 'Data Nilai Plafond tidak ditemukan');
}
$nilaiPlafond->update($validate);
DB::commit();
Log::info('NilaiPlafondController@update: update berhasil', ['id' => $id]);
return \redirect()
return redirect()
->route('basicdata.nilai-plafond.index')
->with('success', 'Nilai Plafond berhasil diperbarui');
->with('success', 'Jenis Aset updated successfully');
} catch (Exception $e) {
DB::rollBack();
Log::error('NilaiPlafondController@update: update gagal', ['id' => $id, 'error' => $e->getMessage()]);
return \redirect()
return redirect()
->route('basicdata.nilai-plafond.edit', $id)
->with('error', 'Gagal memperbarui Nilai Plafond');
->with('error', 'Failed to update nilai plafond');
}
}
}
/**
* Menghapus data Nilai Plafond berdasarkan ID.
* Logging setiap langkah dan gunakan transaksi DB.
*/
public function destroy($id)
{
Log::info('NilaiPlafondController@destroy: mulai proses hapus', ['id' => $id]);
DB::beginTransaction();
try {
// Delete from database
$nilaiPlafond = NilaiPlafond::find($id);
if (!$nilaiPlafond) {
DB::rollBack();
Log::warning('NilaiPlafondController@destroy: data tidak ditemukan', ['id' => $id]);
return \response()->json(['success' => false, 'message' => 'Data Nilai Plafond tidak ditemukan']);
}
$nilaiPlafond->delete();
DB::commit();
Log::info('NilaiPlafondController@destroy: hapus berhasil', ['id' => $id]);
return \response()->json(['success' => true, 'message' => 'Nilai Plafond berhasil dihapus']);
echo json_encode(['success' => true, 'message' => 'Jenis Aset deleted successfully']);
} catch (Exception $e) {
DB::rollBack();
Log::error('NilaiPlafondController@destroy: hapus gagal', ['id' => $id, 'error' => $e->getMessage()]);
return \response()->json(['success' => false, 'message' => 'Gagal menghapus Nilai Plafond']);
echo json_encode(['success' => false, 'message' => 'Failed to delete nilai plafond']);
}
}
/**
* Menyediakan data untuk datatables dengan pencarian, sortir, dan paginasi.
* Logging proses dan gunakan transaksi DB untuk konsistensi pembacaan.
*/
public function dataForDatatables(Request $request)
{
Log::info('NilaiPlafondController@dataForDatatables: mulai proses');
if (is_null($this->user) || !$this->user->can('nilai_plafond.view')) {
//abort(403, 'Sorry! You are not allowed to view users.');
}
DB::beginTransaction();
try {
// Retrieve data from the database
$query = NilaiPlafond::query();
// Retrieve data from the database
$query = NilaiPlafond::query();
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('code', 'LIKE', "%$search%");
$q->orWhere('name', 'LIKE', "%$search%");
});
}
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page
$data = $query->get();
// Calculate the page count
$pageCount = ceil($totalRecords / $request->get('size'));
// Calculate the current page number
$currentPage = 0 + 1;
DB::commit();
Log::info('NilaiPlafondController@dataForDatatables: proses selesai, mengembalikan data');
// Return the response data as a JSON object
return \response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]);
} catch (Exception $e) {
DB::rollBack();
Log::error('NilaiPlafondController@dataForDatatables: gagal memproses data', ['error' => $e->getMessage()]);
return \response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => 0,
'recordsFiltered' => 0,
'pageCount' => 0,
'page' => 1,
'totalCount' => 0,
'data' => [],
]);
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('code', 'LIKE', "%$search%");
$q->orWhere('name', 'LIKE', "%$search%");
});
}
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page
$data = $query->get();
// Calculate the page count
$pageCount = ceil($totalRecords / $request->get('size'));
// Calculate the current page number
$currentPage = 0 + 1;
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]);
}
/**
* Mengekspor data Nilai Plafond ke Excel.
* Log akses sebelum proses download.
*/
public function export()
{
Log::info('NilaiPlafondController@export: mulai proses export');
return Excel::download(new NilaiPlafondExport, 'nilai_plafond.xlsx');
}
}

View File

@@ -6,10 +6,10 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Modules\Lpj\Http\Requests\NocRequest;
use Modules\Lpj\Models\Bucok;
use Modules\Lpj\Models\Noc;
use Modules\Lpj\Models\Noc;
use Modules\Lpj\Models\PenawaranTender;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\PersetujuanPenawaran;
use Modules\Lpj\Models\JenisPenilaian;
class NocController extends Controller
{
@@ -26,15 +26,13 @@ use Modules\Lpj\Models\Noc;
public function pembayaran()
{
$persetujuanPenawarans = PersetujuanPenawaran::all();
$jenisPenilaians = JenisPenilaian::get();
return view('lpj::noc.pembayaran', compact('persetujuanPenawarans', 'jenisPenilaians'));
return view('lpj::noc.pembayaran', compact('persetujuanPenawarans'));
}
public function penyelesaian()
{
$persetujuanPenawarans = PersetujuanPenawaran::all();
$jenisPenilaians = JenisPenilaian::get();
return view('lpj::noc.penyelesaian', compact('persetujuanPenawarans', 'jenisPenilaians'));
return view('lpj::noc.penyelesaian', compact('persetujuanPenawarans'));
}
/**
@@ -49,42 +47,28 @@ use Modules\Lpj\Models\Noc;
public function store(NocRequest $request)
{
$validated = $request->validated();
$validated['updated_by'] = Auth::id();
if (request()->get('status_bayar') == "sudah_bayar") {
$validated['status'] = '1';
$status = "spk";
} else {
$status = "persetujuan-penawaran";
}
$dataNoc = [
'nominal_bayar' => $validated['nominal_bayar'],
'total_pembukuan' => $validated['total_pembukuan'],
'tanggal_pembayaran' => $validated['tanggal_pembayaran'] ?? date('Y-m-d'),
'status_bayar' => $validated['nominal_bayar'] < $validated['total_harus_bayar'] ? false : true,
'catatan_noc' => $validated['catatan_noc'] ?? '',
'status_kurang_bayar' => $validated['status_kurang_bayar'] ?? '0',
'status_lebih_bayar' => $validated['status_lebih_bayar'] ?? '0',
'nominal_kurang_bayar' => $validated['nominal_kurang_bayar'] ?? '0',
'nominal_lebih_bayar' => $validated['nominal_lebih_bayar'] ?? '0',
'bukti_pengembalian' => $validated['bukti_pengembalian'] ?? '',
'catatan_noc' => $validated['catatan_noc'],
];
if($validated['permohonan_id']){
$noc = Noc::updateOrCreate(
$noc = Noc::updateOrCreate(
[
'permohonan_id' => $validated['permohonan_id'],
'persetujuan_penawaran_id' => $validated['persetujuan_penawaran_id'],
],
$dataNoc,
);
} else {
$noc = Noc::updateOrCreate(
[
'persetujuan_penawaran_id' => $validated['persetujuan_penawaran_id'],
],
$dataNoc,
);
}
$folderPath = 'noc/' . request()->get('persetujuan_penawaran_id') . '/bukti_ksl/';
@@ -96,13 +80,26 @@ use Modules\Lpj\Models\Noc;
}
$noc->save();
$bucok = Bucok::where('nomor_tiket', $noc->nomor_tiket)->orWhere('permohonan_id', $noc->permohonan_id)->first();
if($bucok){
$bucok->nominal_penyelesaian = $noc->total_pembukuan ?? '';
$bucok->tanggal_penyelesaian = $noc->tanggal_pembayaran ?? date('Y-m-d');
$bucok->penyelesaian = 'Selesai';
$bucok->save();
}
/* Update the status of the related permohonan to 'spk'
$permohonan = Permohonan::find(request()->get('permohonan_id'));
if ($permohonan) {
$permohonan->status_bayar = request()->get('status_pembayar');
if ($permohonan->jenis_penilaian_id == 2) {
$permohonan->status = $status;
}
$permohonan->save();
// andy add, update status penawaran.status='spk'
// $penawaran = PenawaranTender::where('nomor_registrasi',$permohonan->nomor_registrasi)->first();
if ($permohonan->jenis_penilaian_id == 2) {
PenawaranTender::where('nomor_registrasi', $permohonan->nomor_registrasi)->update([
'status' => $status,
'updated_by' => Auth::id(),
'updated_at' => now(),
]);
}
// andy add, update status penawaran.status='spk'
}*/
return redirect()
->route('noc.index')->with('success', 'NOC berhasil disimpan.');
@@ -115,7 +112,6 @@ use Modules\Lpj\Models\Noc;
{
$validated = $request->validated();
if($request->get('is_memo')){
$memo = Noc::find($request->get('is_memo'));
@@ -137,7 +133,6 @@ use Modules\Lpj\Models\Noc;
->route('laporan.index')->with('success', 'Memo Penyelesaian updated successfully');
}
$dataNoc = [
'total_pembukuan' => $validated['total_pembukuan'],
'nominal_penyelesaian' => $validated['nominal_penyelesaian'],
'tanggal_penyelesaian' => $validated['tanggal_penyelesaian'] ?? date('Y-m-d'),
'status_pelunasan' => ((int)$validated['nominal_bayar'] + (int)$validated['nominal_penyelesaian']) === (int)$validated['total_harus_bayar'] ? true : false,
@@ -220,19 +215,15 @@ use Modules\Lpj\Models\Noc;
$query = PersetujuanPenawaran::query();
// Filter for pembayaran (where memo_penyelesaian is null)
/*$query->whereDoesntHave('noc', function($q) {
$query->whereDoesntHave('noc', function($q) {
$q->whereNotNull('memo_penyelesaian');
});*/
});
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->orWhereRelation('penawaran', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
->orWhereRelation('permohonan', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
->orWhereRelation('permohonan.debiture','name', 'LIKE', '%' . $search . '%')
->orWhereRelation('permohonan.jenisPenilaian', 'name', 'LIKE', '%' . $search . '%')
->orWhere('nomor_tiket', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('penawaran', 'nomor_registrasi', 'LIKE', '%' . $search . '%');
});
}
@@ -264,11 +255,9 @@ use Modules\Lpj\Models\Noc;
$data = $data->map(function ($persetujuanPenawaran) {
return [
'id' => $persetujuanPenawaran->id,
'nomor_registrasi' => $persetujuanPenawaran->permohonan?->nomor_registrasi ?? $persetujuanPenawaran->penawaran?->nomor_registrasi,
'nomor_tiket' => $persetujuanPenawaran->nomor_tiket ?? '',
'nama_debitur' => $persetujuanPenawaran?->permohonan?->debiture->name ?? $persetujuanPenawaran->penawaran?->permohonan?->debiture->name ?? $persetujuanPenawaran->noc?->debiture->name,
'kode_cabang' => $persetujuanPenawaran?->permohonan?->branch->code ?? $persetujuanPenawaran->penawaran?->permohonan?->branch->code ?? $persetujuanPenawaran->noc?->branch->code,
'cabang' => $persetujuanPenawaran?->permohonan?->branch->name ?? $persetujuanPenawaran->penawaran?->permohonan?->branch->name ?? $persetujuanPenawaran->noc?->branch->name,
'nomor_registrasi' => $persetujuanPenawaran->permohonan->nomor_registrasi ?? $persetujuanPenawaran->penawaran->nomor_registrasi,
'nama_debitur' => $persetujuanPenawaran->permohonan->debiture->name ?? $persetujuanPenawaran->penawaran->permohonan->debiture->name,
'cabang' => $persetujuanPenawaran->permohonan->branch->name ?? $persetujuanPenawaran->penawaran->permohonan->branch->name,
'tanggal_pembayaran' => dateFormat(
$persetujuanPenawaran->noc->tanggal_pembayaran ?? $persetujuanPenawaran->noc?->created_at,
true,
@@ -278,19 +267,11 @@ use Modules\Lpj\Models\Noc;
'nominal_diterima' => currencyFormat(
$persetujuanPenawaran->noc->nominal_bayar ?? 0,
),
'jenis_penilaian' => $persetujuanPenawaran->permohonan?->jenisPenilaian?->name ?? "",
'bukti_ksl' => $persetujuanPenawaran->noc->bukti_ksl ?? $persetujuanPenawaran->bukti_ksl ?? null,
'bukti_bayar' => $persetujuanPenawaran->bukti_bayar ?? null,
'updated_at' => dateFormat($persetujuanPenawaran->updated_at, true),
];
})->sortBy('updated_at', 1)->values();
// Calculate total nominal diterima from all filtered data (not just current page)
$totalNominalDiterima = $data->sum(function ($item) {
// Extract numeric value from formatted currency string
$nominal = str_replace(['Rp', '.', ',00'], '', $item['nominal_diterima']);
return (float) $nominal;
});
})->sortBy('updated_at', 1);
// Calculate the page count
$pageCount = ceil($totalRecords / $request->get('size'));
@@ -306,7 +287,6 @@ use Modules\Lpj\Models\Noc;
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'totalNominalDiterima' => $totalNominalDiterima,
'data' => $data,
]);
}
@@ -321,7 +301,7 @@ use Modules\Lpj\Models\Noc;
$query = PersetujuanPenawaran::query();
// Filter for penyelesaian (where memo_penyelesaian is not null)
$query->whereDoesntHave('noc', function($q) {
$query->whereHas('noc', function($q) {
$q->whereNotNull('memo_penyelesaian');
});
@@ -329,15 +309,7 @@ use Modules\Lpj\Models\Noc;
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->orWhereRelation('penawaran', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
->orWhereRelation('permohonan', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
->orWhereRelation('permohonan.debiture','name', 'LIKE', '%' . $search . '%')
->orWhereRelation('permohonan.jenisPenilaian', 'name', 'LIKE', '%' . $search . '%')
->orWhere('nomor_tiket', 'LIKE', '%' . $search . '%'); $q->orWhereRelation('penawaran', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
->orWhereRelation('permohonan', 'nomor_registrasi', 'LIKE', '%' . $search . '%')
->orWhereRelation('permohonan.debiture','name', 'LIKE', '%' . $search . '%')
->orWhereRelation('permohonan.jenisPenilaian', 'name', 'LIKE', '%' . $search . '%')
->orWhere('nomor_tiket', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('penawaran', 'nomor_registrasi', 'LIKE', '%' . $search . '%');
});
}
@@ -369,11 +341,9 @@ use Modules\Lpj\Models\Noc;
$data = $data->map(function ($persetujuanPenawaran) {
return [
'id' => $persetujuanPenawaran->id,
'nomor_registrasi' => $persetujuanPenawaran->permohonan?->nomor_registrasi ?? $persetujuanPenawaran->penawaran?->nomor_registrasi ?? '',
'nomor_tiket' => $persetujuanPenawaran->nomor_tiket ?? '',
'nama_debitur' => $persetujuanPenawaran->permohonan?->debiture?->name ?? $persetujuanPenawaran->penawaran?->permohonan?->debiture?->name ?? $persetujuanPenawaran->noc?->debiture?->name,
'kode_cabang' => $persetujuanPenawaran?->permohonan?->branch?->code ?? $persetujuanPenawaran->penawaran?->permohonan?->branch?->code ?? $persetujuanPenawaran->noc?->branch?->code ?? '',
'cabang' => $persetujuanPenawaran->permohonan?->branch?->name ?? $persetujuanPenawaran->penawaran?->permohonan?->branch?->name ?? $persetujuanPenawaran->noc?->branch?->name ?? '',
'nomor_registrasi' => $persetujuanPenawaran->permohonan->nomor_registrasi ?? $persetujuanPenawaran->penawaran->nomor_registrasi,
'nama_debitur' => $persetujuanPenawaran->permohonan->debiture->name ?? $persetujuanPenawaran->penawaran->permohonan->debiture->name,
'cabang' => $persetujuanPenawaran->permohonan->branch->name ?? $persetujuanPenawaran->penawaran->permohonan->branch->name,
'tanggal_pembayaran' => dateFormat(
$persetujuanPenawaran->noc->tanggal_pembayaran ?? $persetujuanPenawaran->noc?->created_at,
true,
@@ -383,7 +353,6 @@ use Modules\Lpj\Models\Noc;
'nominal_diterima' => currencyFormat(
$persetujuanPenawaran->noc->nominal_bayar ?? 0,
),
'jenis_penilaian' => $persetujuanPenawaran->permohonan?->jenisPenilaian?->name ?? "",
'bukti_ksl' => $persetujuanPenawaran->noc->bukti_ksl ?? $persetujuanPenawaran->bukti_ksl ?? null,
'bukti_bayar' => $persetujuanPenawaran->bukti_bayar ?? null,
'memo_penyelesaian' => $persetujuanPenawaran->noc->memo_penyelesaian ?? $persetujuanPenawaran->memo_penyelesaian ?? null,
@@ -396,15 +365,7 @@ use Modules\Lpj\Models\Noc;
true) : '-',
'updated_at' => dateFormat($persetujuanPenawaran->updated_at, true),
];
})->sortBy('updated_at', 1)->values();
// Calculate total nominal diterima from all filtered data (not just current page)
$totalNominalDiterima = $data->sum(function ($item) {
// Extract numeric value from formatted currency string
$nominal = str_replace(['Rp', '.', ',00'], '', $item['nominal_diterima']);
return (float) $nominal;
});
})->sortBy('updated_at', 1);
// Calculate the page count
$pageCount = ceil($totalRecords / $request->get('size'));
@@ -420,7 +381,6 @@ use Modules\Lpj\Models\Noc;
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'totalNominalDiterima' => $totalNominalDiterima,
'data' => $data,
]);
}

View File

@@ -3,12 +3,26 @@
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Barryvdh\DomPDF\Facade\Pdf;
use Exception;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Location\Models\City;
use Modules\Location\Models\District;
use Modules\Location\Models\Province;
use Modules\Location\Models\Village;
use Modules\Lpj\Exports\PermohonanExport;
use Modules\Lpj\Http\Requests\PermohonanRequest;
use Modules\Lpj\Models\Branch;
use Modules\Lpj\Models\Debiture;
use Modules\Lpj\Models\DokumenJaminan;
use Modules\Lpj\Models\JenisFasilitasKredit;
use Modules\Lpj\Models\NilaiPlafond;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\PermohonanPembatalan;
use Modules\Lpj\Models\StatusPermohonan;
use Modules\Lpj\Models\TujuanPenilaian;
use Modules\Lpj\Services\PermohonanHistoryService;
use Illuminate\Support\Facades\Auth;
class PembatalanController extends Controller
{
@@ -78,10 +92,6 @@
// Retrieve data from the database
$query = PermohonanPembatalan::query();
if (Auth::user()->hasAnyRole(['pemohon-ao','pemohon-eo'])) {
$query = $query->whereRelation('permohonan', 'branch_id', Auth::user()->branch_id);
}
$query = $query->orderBy('created_at', 'desc');
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {

View File

@@ -2,17 +2,21 @@
namespace Modules\Lpj\Http\Controllers;
use Exception;
use Modules\Lpj\Models\Noc;
use Illuminate\Http\Request;
use Modules\Lpj\Models\Bucok;
use Illuminate\Http\JsonResponse;
use Modules\Lpj\Models\Permohonan;
use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Modules\Lpj\Models\PenawaranTender;
use Modules\Lpj\Models\PersetujuanPenawaran;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Lpj\Http\Requests\PersetujuanPenawaranRequest;
use Modules\Lpj\Models\PenawaranTender;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\PersetujuanPenawaran;
// use Modules\Lpj\Models\JenisPenilaian;
// use Modules\Lpj\Models\Regions;
class PembayaranController extends Controller
{
@@ -23,14 +27,6 @@ class PembayaranController extends Controller
return view('lpj::pembayaran.index');
}
public function kurang(){
return view('lpj::pembayaran.kurang');
}
public function lebih(){
return view('lpj::pembayaran.lebih');
}
public function approval()
{
return view('lpj::pembayaran.approval');
@@ -39,16 +35,13 @@ class PembayaranController extends Controller
public function dataApprovalForDatatables(Request $request)
{
if (is_null($this->user) || !$this->user->can('noc.view')) {
//abort(403, 'Sorry! You are not allowed to view persetujuan penawaran.');
//abort(403, 'Sorry! You are not allowed to view persetujuan penawaran.');
}
// Retrieve data from the database
// Retrieve data from the database
$query = PersetujuanPenawaran::query();
if (Auth::user()->hasAnyRole(['pemohon-ao','pemohon-eo'])) {
$query = $query->whereRelation('permohonan', 'branch_id', Auth::user()->branch_id);
}
// Apply search filter if provided
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
@@ -56,29 +49,29 @@ class PembayaranController extends Controller
});
}
// Apply sorting if provided
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Get the total count of records
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the filtered count of records
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page
// Get the data for the current page
$data = $query
->with(
[
@@ -96,13 +89,13 @@ class PembayaranController extends Controller
)->get();
// Calculate the page count
// Calculate the page count
$pageCount = ceil($totalRecords / $request->get('size'));
// Calculate the current page number
// Calculate the current page number
$currentPage = $request->get('page', 1);
// Return the response data as a JSON object
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
@@ -114,130 +107,34 @@ class PembayaranController extends Controller
]);
}
public function create(){
return view('lpj::pembayaran.create');
}
public function edit($id)
{
$permohonan = Permohonan::find($id);
$req = request()->all();
if(isset($req['tiket'])){
$persetujuanPenawaran = PersetujuanPenawaran::find($id);
$permohonan = Permohonan::find($persetujuanPenawaran?->permohonan_id);
} else {
$permohonan = Permohonan::find($id);
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $permohonan->id)->first();
}
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $permohonan->id)->first();
return view('lpj::pembayaran.form', compact('permohonan', 'persetujuanPenawaran'));
}
public function editKurang($id){
$noc = Noc::find($id);
$permohonan = Permohonan::find($noc->permohonan_id);
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $permohonan->id)->first();
return view('lpj::pembayaran.form-kurang', compact('noc','permohonan','persetujuanPenawaran'));
}
public function editLebih($id){
$noc = Noc::find($id);
$permohonan = Permohonan::find($noc->permohonan_id);
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $permohonan->id)->first();
return view('lpj::pembayaran.form-lebih', compact('noc','permohonan','persetujuanPenawaran'));
}
public function store(PersetujuanPenawaranRequest $request)
{
$req = request()->all();
if(isset($req['type'])){
if($req['type'] == 'create'){
$data = [
'nomor_tiket' => $req['nomor_tiket'] ?? '',
'nominal_bayar' => $req['nominal_bayar'] ?? '',
'catatan' => $req['catatan'] ?? ''
];
if(request()->hasFile('bukti_bayar')){
$folderPath = 'persetujuan_penawaran/bukti_bayar/' . $req['nomor_tiket'];
$data['bukti_bayar'] = $request->file('bukti_bayar')->store($folderPath, 'public');
}
$persetujuanPenawaran = PersetujuanPenawaran::create($data);
$noc = [
'persetujuan_penawaran_id' => $persetujuanPenawaran->id,
'nomor_tiket' => $req['nomor_tiket'] ?? '',
'debiture_id' => $req['debitur_id'] ?? '',
'branch_id' => Auth::user()->branch_id,
];
$noc = Noc::create($noc);
$bucok = [
'tanggal_penuh' => $persetujuanPenawaran->created_at ?? $noc->created_at,
'tanggal' => $persetujuanPenawaran->created_at?->format('d') ?? $noc->created_at?->format('d'),
'bulan' => $persetujuanPenawaran->created_at?->format('m') ?? $noc->created_at?->format('m'),
'tahun' => $persetujuanPenawaran->created_at?->format('Y') ?? $noc->created_at?->format('Y'),
'nomor_tiket' => $req['nomor_tiket'] ?? '',
'nominal' => $req['nominal_bayar'] ?? '',
'nominal_berjalan' => $req['nominal_bayar'] ?? '',
'penyelesaian' => 'Belum Selesai',
'nama_sub_direktorat' => $noc->branch?->name ?? '',
'nama_direktorat_cabang' => $noc->branch?->name ?? '',
];
Bucok::updateOrCreate([
'nomor_tiket' => $req['nomor_tiket'] ?? '',
], $bucok);
return redirect()
->route('pembayaran.index')->with('success', 'Pembayaran berhasil disimpan.');
}
if($req['type'] == 'kurang_bayar'){
$noc = Noc::find($req['noc_id']);
$noc->nominal_pelunasan = $req['nominal_pelunasan'];
if (request()->hasFile('bukti_ksl_kurang_bayar')) {
$folderPath = 'persetujuan_penawaran/bukti_ksl_kurang_bayar/' . $req['noc_id'];
$noc->bukti_ksl_kurang_bayar = $request->file('bukti_ksl_kurang_bayar')->store($folderPath, 'public');
}
$noc->save();
$persetujuanPenawaran = PersetujuanPenawaran::find($noc->persetujuan_penawaran_id);
$persetujuanPenawaran->bukti_ksl_kurang_bayar = $noc->bukti_ksl_kurang_bayar;
$persetujuanPenawaran->nominal_kurang_bayar = $req['nominal_pelunasan'];
$persetujuanPenawaran->save();
return redirect()
->route('pembayaran.kurang.index')->with('success', 'Pelunasan Kurang Bayar berhasil disimpan.');
}
if($req['type'] == 'lebih_bayar'){
$noc = Noc::find($req['noc_id']);
if (request()->hasFile('bukti_ksl_lebih_bayar')) {
$folderPath = 'persetujuan_penawaran/bukti_ksl_lebih_bayar/' . $req['noc_id'];
$noc->bukti_ksl_lebih_bayar = $request->file('bukti_ksl_lebih_bayar')->store($folderPath, 'public');
}
$noc->save();
return redirect()
->route('pembayaran.lebih.index')->with('success', 'Pengembalian Lebih Bayar berhasil disimpan.');
}
}
$validated = $request->validated();
$validated['nominal_bayar'] = $req['nominal_bayar'] ?? 0;
$validated['created_by'] = Auth::id();
$validated['created_at'] = now();
$validated['status'] = '0';
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $validated['permohonan_id'] ?? null)->first();
$permohonan = Permohonan::find(request()->get('permohonan_id'));
$permohonan = Permohonan::find(request()->get('permohonan_id'));
if ($persetujuanPenawaran) {
// if (isset($validated['penawaran_id'])) {
// $persetujuanPenawaran = PersetujuanPenawaran::create(
// ['penawaran_id' => $validated['penawaran_id']],
// $validated,
// );
$persetujuanPenawaran->fill($validated);
if ($request->hasFile('bukti_bayar')) {
$folderPath = 'persetujuan_penawaran/' . $validated['penawaran_id'];
$folderPath = 'persetujuan_penawaran/' . $validated['penawaran_id'];
$persetujuanPenawaran->bukti_bayar = $request->file('bukti_bayar')->store($folderPath, 'public');
}
@@ -245,22 +142,13 @@ class PembayaranController extends Controller
$permohonan->approve_bayar_by = null;
$permohonan->approve_bayar_at = null;
$permohonan->status = 'proses-laporan';
$permohonan->status = 'done';
$permohonan->save();
} else {
$persetujuanPenawaran = PersetujuanPenawaran::create(
$validated
);
if(isset($validated['nomor_tiket'])){
$noc = Noc::where('nomor_tiket',$validated['nomor_tiket'])->first();
if($noc){
$noc->persetujuan_penawaran_id = $persetujuanPenawaran->id;
$noc->permohonan_id = $validated['permohonan_id'];
$noc->save();
}
}
$folderPath = 'persetujuan_penawaran/' . $validated['penawaran_id'];
if ($request->hasFile('bukti_bayar')) {
@@ -269,47 +157,20 @@ class PembayaranController extends Controller
$persetujuanPenawaran->save();
}
$bucok = [
'tanggal_penuh' => $persetujuanPenawaran->created_at ?? $validated['created_at'],
'tanggal' => $persetujuanPenawaran->created_at?->format('d') ?? $validated['created_at']?->format('d'),
'bulan' => $persetujuanPenawaran->created_at?->format('m') ?? $validated['created_at']?->format('m'),
'tahun' => $persetujuanPenawaran->created_at?->format('Y') ?? $validated['created_at']?->format('Y'),
'nomor_tiket' => $req['nomor_tiket'] ?? '',
'nominal' => $req['nominal_bayar'] ?? '',
'nominal_berjalan' => $req['nominal_bayar'] ?? '',
'penyelesaian' => 'Belum Selesai',
'nama_sub_direktorat' => $noc->branch?->name ?? '',
'nama_direktorat_cabang' => $noc->branch?->name ?? '',
'permohonan_id' => $permohonan->id,
'nomor_registrasi' => $permohonan->nomor_registrasi,
];
if(isset($req['nomor_tiket']) && $req['nomor_tiket'] !=''){
Bucok::updateOrCreate([
'nomor_registrasi' => $permohonan->nomor_registrasi,
'nomor_tiket' => $req['nomor_tiket'],
], $bucok);
} else {
Bucok::updateOrCreate([
'nomor_registrasi' => $permohonan->nomor_registrasi
], $bucok);
}
// Update the status of the related permohonan to 'spk'
// Update the status of the related permohonan to 'spk'
if ($permohonan) {
$permohonan->status_bayar = request()->get('status_bayar');
$permohonan->save();
// andy add, update status penawaran.status='spk'
// $penawaran = PenawaranTender::where('nomor_registrasi',$permohonan->nomor_registrasi)->first();
// andy add, update status penawaran.status='spk'
// $penawaran = PenawaranTender::where('nomor_registrasi',$permohonan->nomor_registrasi)->first();
PenawaranTender::where('nomor_registrasi', $permohonan->nomor_registrasi)->update([
'status' => 'noc',
'updated_by' => Auth::id(),
'updated_at' => now(),
]);
// andy add, update status penawaran.status='spk'
// andy add, update status penawaran.status='spk'
}
@@ -319,7 +180,7 @@ class PembayaranController extends Controller
public function update(Request $request, $id): JsonResponse
{
// init
// init
$data = [];
$output = [];
$tindakan = null;
@@ -335,22 +196,22 @@ class PembayaranController extends Controller
}
$output['data'] = $data;
// Update the status of the related permohonan to 'spk'
// Update the status of the related permohonan to 'spk'
$permohonan = Permohonan::find($id);
if ($permohonan) {
if ($request->type === 'revisi') {
$data['status'] = 'revisi-pembayaran';
$data['status'] = 'revisi-pembayaran';
$data['status_bayar'] = 'belum_bayar';
} else {
$data['status_bayar'] = 'sudah_bayar';
$data['status'] = 'proses-laporan';
$data['status'] = 'proses-laporan';
}
if ($permohonan->jenis_penilaian_id == 2) {
$data['status_bayar'] = 'sudah_bayar';
$data['status'] = 'spk';
}
if ($permohonan->jenis_penilaian_id == 2) {
$data['status_bayar'] = 'sudah_bayar';
$data['status'] = 'spk';
}
if ($permohonan->jenis_penilaian_id == 1) {
@@ -390,43 +251,47 @@ class PembayaranController extends Controller
public function dataForDatatables(Request $request)
{
if (is_null($this->user) || !$this->user->can('debitur.view')) {
// abort(403, 'Sorry! You are not allowed to view users.');
// abort(403, 'Sorry! You are not allowed to view users.');
}
$query = PersetujuanPenawaran::query();
if (Auth::user()->hasAnyRole(['pemohon-ao','pemohon-eo'])) {
$query = $query->whereRelation('permohonan', 'branch_id', Auth::user()->branch_id);
}
/*$query->where(function($q) {
$q->whereRelation('permohonan', function($query) {
$query->where('status_bayar', 'belum_bayar')
->where('jenis_penilaian_id', 1);
$query = Permohonan::query()->where(function ($query) {
$query->where(['status_bayar' => 'belum_bayar', 'jenis_penilaian_id' => 1])
->orWhere('status', 'revisi-pembayaran');
})
->where(function ($query) {
$query->whereNotIn('id', function ($subquery) {
$subquery->select('permohonan_id')
->from('persetujuan_penawaran')
->whereNotNull('permohonan_id');
});
});*/
$query->orWhereRelation('permohonan','status_bayar','revisi-pembayaran');
$query->orWhere(function($q) {
$q->where('permohonan_id',null);
$q->where('nomor_tiket','!=',null);
});
// Pencarian berdasarkan parameter search
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('nomor_registrasi', 'LIKE', '%' . $search . '%');
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('debiture', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%');
$q->orWhere('status', 'LIKE', '%' . $search . '%');
});
}
// Sorting berdasarkan sortField dan sortOrder
// Sorting berdasarkan sortField dan sortOrder
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Hitung total records
// Hitung total records
$totalRecords = $query->count();
// Pagination (default page size 10)
// Pagination (default page size 10)
$size = $request->get('size', 10);
if ($size == 0) {
$size = 10;
@@ -439,172 +304,20 @@ class PembayaranController extends Controller
$query->skip($offset)->take($size);
}
// Filtered records
// Filtered records
$filteredRecords = $query->count();
// Ambil data dengan relasi
$data = $query->get();
$data = $data->map(function ($item) {
return [
'id' => $item->permohonan?->id ?? $item->id,
'nomor_registrasi' => $item->permohonan?->nomor_registrasi,
'nomor_tiket' => $item->nomor_tiket ?? '',
'debiture' => $item->permohonan?->debiture ?? $item->noc?->debiture,
'user' => $item->permohonan?->user ?? $item->creator,
'status_bayar' => $item->permohonan?->status_bayar ?? ($item->nomor_tiket ? 'Sudah Bayar' : ''),
'tanggal_permohonan' => $item->permohonan?->tanggal_permohonan ?? '',
'branch' => $item->permohonan?->branch ?? $item->noc?->branch,
'is_permohonan' => $item->permohonan ?? ''
];
});
// Ambil data dengan relasi
$data = $query->with(['user', 'debiture', 'branch', 'jenisPenilaian'])->get();
// Hitung jumlah halaman
// Hitung jumlah halaman
$pageCount = ceil($totalRecords / $size);
// Ambil current page
// Ambil current page
$currentPage = max(1, $request->get('page', 1));
// Return JSON response
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]);
}
public function dataForDatatablesKurang(Request $request)
{
if (is_null($this->user) || !$this->user->can('debitur.view')) {
// abort(403, 'Sorry! You are not allowed to view users.');
}
$query = Noc::query()->where(function ($query) {
$query->where(['status_kurang_bayar' => '1'])
->where('bukti_ksl_kurang_bayar',null);
});
// Sorting berdasarkan sortField dan sortOrder
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Hitung total records
$totalRecords = $query->count();
// Pagination (default page size 10)
$size = $request->get('size', 10);
if ($size == 0) {
$size = 10;
}
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page', 1);
$offset = ($page - 1) * $size;
$query->skip($offset)->take($size);
}
// Filtered records
$filteredRecords = $query->count();
// Ambil data dengan relasi
$data = $query->get();
$data = $data->map(function ($item) {
return [
'id' => $item->id,
'permohonan' => $item->permohonan,
'pemohon' => $item->permohonan->user,
'branch' => $item->permohonan->branch,
'debiture' => $item->permohonan->debiture,
'nominal_kurang_bayar' => formatRupiah($item->nominal_kurang_bayar,2)
];
});
// Hitung jumlah halaman
$pageCount = ceil($totalRecords / $size);
// Ambil current page
$currentPage = max(1, $request->get('page', 1));
// Return JSON response
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $data,
]);
}
public function dataForDatatablesLebih(Request $request)
{
if (is_null($this->user) || !$this->user->can('debitur.view')) {
// abort(403, 'Sorry! You are not allowed to view users.');
}
$query = Noc::query()->where(function ($query) {
$query->where(['status_lebih_bayar' => '1'])
->where('bukti_ksl_lebih_bayar',null);
});
// Sorting berdasarkan sortField dan sortOrder
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
}
// Hitung total records
$totalRecords = $query->count();
// Pagination (default page size 10)
$size = $request->get('size', 10);
if ($size == 0) {
$size = 10;
}
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page', 1);
$offset = ($page - 1) * $size;
$query->skip($offset)->take($size);
}
// Filtered records
$filteredRecords = $query->count();
// Ambil data dengan relasi
$data = $query->get();
$data = $data->map(function ($item) {
return [
'id' => $item->id,
'permohonan' => $item->permohonan,
'pemohon' => $item->permohonan->user,
'branch' => $item->permohonan->branch,
'debiture' => $item->permohonan->debiture,
'nominal_lebih_bayar' => formatRupiah($item->nominal_lebih_bayar,2)
];
});
// Hitung jumlah halaman
$pageCount = ceil($totalRecords / $size);
// Ambil current page
$currentPage = max(1, $request->get('page', 1));
// Return JSON response
// Return JSON response
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,

View File

@@ -157,7 +157,7 @@ class PenilaiController extends Controller
$permohonan = $this->surveyorController->getPermohonanJaminanId($id, $documentId, $jaminanId);
if ($permohonan->status == 'proses-laporan') {
//return redirect()->back()->with('error', 'Masih dalam proses laporan');
return redirect()->back()->with('error', 'Masih dalam proses laporan');
}
$basicData = $this->surveyorController->getCommonData();
@@ -619,8 +619,7 @@ class PenilaiController extends Controller
'jenisfasilitasKredit',
'penilaian.userPenilai',
'penilai',
'nilaiPlafond',
'penawaran'
'nilaiPlafond'
])->get();
// Calculate the page count
@@ -841,10 +840,10 @@ class PenilaiController extends Controller
$permohonan = Permohonan::findOrFail($id);
if ($permohonan->status === 'proses-laporan') {
/*return response()->json([
return response()->json([
'success' => false,
'message' => 'Masih proses laporan',
], 400);*/
], 400);
}
if ($permohonan->status === 'proses-paparan') {
@@ -1006,10 +1005,11 @@ class PenilaiController extends Controller
],
];
Inspeksi::updateOrCreate([
Inspeksi::create([
'permohonan_id' => $validatedData['permohonan_id'],
'dokument_id' => $validatedData['dokument_id']
],[
'dokument_id' => $validatedData['dokument_id'],
'data_form' => json_encode($newData),
'name' => $validatedData['type']
]);
@@ -1251,10 +1251,9 @@ class PenilaiController extends Controller
Inspeksi::updateOrCreate([
Inspeksi::create([
'permohonan_id' => $validated['permohonan_id'],
'dokument_id' => $validated['dokument_id']
],[
'dokument_id' => $validated['dokument_id'],
'data_form' => json_encode($newData),
'name' => $validated['type']
]);
@@ -1275,141 +1274,15 @@ class PenilaiController extends Controller
}
}
public function print_out(Request $request)
{
$documentId = $request->query('documentId');
$jaminanId = $request->query('jaminanId');
$permohonanId = $request->query('permohonanId');
$statusLpj = $request->query('statusLpj');
$tipeLaporan = $request->query('type');
$permohonan = $this->surveyorController->getPermohonanJaminanId(
$permohonanId,
$documentId,
$jaminanId
);
$basicData = $this->surveyorController->getCommonData();
$inspeksi = Inspeksi::where('permohonan_id', $permohonanId)->where('dokument_id', $documentId)->first();
$lpj = Penilai::where('permohonan_id', $permohonanId)->where('dokument_id', $documentId)->first();
$nomorLaporan = getNomorLaporan($permohonanId, $documentId);
$tanggalLaporan = $lpj->created_at ?? null;
$forminspeksi = null;
$lpjData = null;
$formFoto = null;
if ($inspeksi) {
$forminspeksi = json_decode($inspeksi->data_form, true);
$formFoto = json_decode($inspeksi->foto_form, true);
// $denahForm = json_decode($data->denah_form, true);
$dataPembanding = json_decode($inspeksi->data_pembanding, true);
}
if ($lpj) {
$lpjData = json_decode($lpj->lpj, true);
$memo = json_decode($lpj->memo, true);
$resumeData = json_decode($lpj->resume, true);
$rap = json_decode($lpj->rap, true);
$report = json_decode($lpj->call_report, true);
}
$inputAddress = $forminspeksi['asset']['alamat']['sesuai'] ?? $forminspeksi['asset']['alamat']['tidak sesuai'] ?? [];
$alamat = [
'address' => $inputAddress['address'] ?? null,
'village_code' => getWilayahName($inputAddress['village_code'] ?? null, 'village'),
'district_code' => getWilayahName($inputAddress['district_code'] ?? null, 'district'),
'city_code' => getWilayahName($inputAddress['city_code'] ?? null, 'city'),
'province_code' => getWilayahName($inputAddress['province_code'] ?? null, 'province')
];
$viewLaporan = $this->getViewLaporan($tipeLaporan);
try {
if ($statusLpj) {
// $pdf = view('lpj::' . $viewLaporan, compact(
$pdf = PDF::loadView('lpj::' . $viewLaporan, compact(
'permohonan',
'forminspeksi',
'lpjData',
'formFoto',
'basicData',
'inspeksi',
'lpj',
'statusLpj',
'alamat',
'dataPembanding',
'nomorLaporan',
'memo',
'resumeData',
'tanggalLaporan',
'rap',
'report'
));
// return $pdf;
$cleanNomorLaporan = str_replace(['/', '\\'], '-', $nomorLaporan);
$pdf->setPaper('A4', 'portrait');
$pdf->set_option('isHtml5ParserEnabled', true);
$pdf->set_option('isPhpEnabled', true);
return response($pdf->output(), 200)
->header('Content-Type', 'application/pdf')
->header('Content-Disposition', 'inline; filename="Laporan_' . $tipeLaporan . '_' . $permohonan->debiture->name . '_' . $cleanNomorLaporan . '.pdf"');
// return $pdf->stream();
} else {
// $pdf = view('lpj::' . $viewLaporan, compact(
$pdf = PDF::loadView('lpj::' . $viewLaporan, compact(
'permohonan',
'forminspeksi',
'lpjData',
'formFoto',
'basicData',
'inspeksi',
'lpj',
'statusLpj',
'alamat',
'dataPembanding',
'nomorLaporan',
'memo',
'resumeData',
'tanggalLaporan',
'rap',
'report'
));
// return $pdf;
$cleanNomorLaporan = str_replace(['/', '\\'], '-', $nomorLaporan);
$pdf->setPaper('A4', 'portrait');
$pdf->set_option('isHtml5ParserEnabled', true);
$pdf->set_option('isPhpEnabled', true);
return $pdf->download('Laporan_' . $tipeLaporan . '_' . $permohonan->debiture->name . '_' . $cleanNomorLaporan . '_data.pdf');
}
} catch (\Exception $e) {
Log::error('PDF generation failed: ' . $e->getMessage());
return response()->json(['error' => 'Failed to generate PDF. Please check the log for details.' . $e->getMessage()], 500);
}
}
public function print_out_laporan($permohonan_id, $document_id, $jaminan_id)
{
// jika tidak ada id kembalikan ke halaman sebelumnya
if (!$permohonan_id || !$document_id || !$jaminan_id) {
return redirect()->back()->with('error', 'Laporan tidak valid');
}
return $this->previewLaporanService->printOutLaporan($permohonan_id, $document_id, $jaminan_id);
// jika tidak ada id kembalikan ke halaman sebelumnya
if (!$permohonan_id || !$document_id || !$jaminan_id) {
return redirect()->back()->with('error', 'Laporan tidak valid');
}
private function getViewLaporan($tipe)
{
$viewMap = [
'sederhana' => 'penilai.components.print-out-sederhana',
'standar' => 'penilai.components.print-out-standar',
'resume' => 'penilai.components.print-resume',
'memo' => 'penilai.components.print-memo',
'rap' => 'penilai.components.print-out-rap',
'call-report' => 'penilai.components.print-out-call-report'
];
return $viewMap[$tipe] ?? 'penilai.components.print-resume';
return $this->previewLaporanService->printOutLaporan($permohonan_id, $document_id, $jaminan_id);
}
@@ -1436,46 +1309,7 @@ class PenilaiController extends Controller
];
}
public function checkPrintOutLaporan(Request $request)
{
$permohonanId = $request->query('permohonanId');
$documentId = $request->query('documentId');
// Ambil data berdasarkan ID
$statusLpj = Penilai::where('permohonan_id', $permohonanId)
->where('dokument_id', $documentId)
->first();
// Jika data tidak ditemukan, kembalikan status null
if (!$statusLpj) {
return response()->json(['status' => null]);
}
// Tentukan tipe berdasarkan kondisi
$type = $statusLpj->type_penilai ?? null;
if ($type === 'memo') {
return $this->checkDataMemo($type, $statusLpj);
}
if ($type === 'resume') {
return $this->checkDataResume($type, $statusLpj);
}
if ($type === 'standar' || $type === 'sederhana') {
return $this->checkDataLpj($type, $statusLpj);
}
if ($type === 'rap') {
return $this->checkDataRap($type, $statusLpj);
}
// Kembalikan respons dengan tipe yang sesuai
return response()->json(['status' => $type]);
}
public function generateNoLaporan($permohonan, $documentId, $type)
@@ -1515,239 +1349,6 @@ class PenilaiController extends Controller
}
public function checkDataMemo($type, $statusLpj)
{
// Ambil data JSON dari statusLpj
$data = json_decode($statusLpj->memo, true) ?? [];
$validationRules = [
'memo' => [
'kepada',
'dari',
'nomor_memo',
'tanggal',
'perihal',
'jenis_asset_tidak_sesuai',
'lokasi.lokasi',
'lokasi.province_code',
'lokasi.city_code',
'lokasi.district_code',
'lokasi.village_code',
'lokasi.penilai',
'terlampir',
'hasil_survey',
'kesimpulan_saran',
],
];
// Validasi data JSON
if (isset($validationRules[$type])) {
$missingFields = [];
foreach ($validationRules[$type] as $field) {
$keys = explode('.', $field);
$value = $data;
foreach ($keys as $key) {
if (!isset($value[$key])) {
$missingFields[] = $field;
break;
}
$value = $value[$key];
}
}
// Jika ada field yang kosong, kembalikan error
if (!empty($missingFields)) {
return response()->json([
'status' => null,
'message' => "Silahkan lengkapi data memo terlebih dahulu.",
'missing_fields' => $missingFields,
], 400);
}
}
// Jika data valid
return response()->json([
'status' => $type,
'message' => "Data memo valid.",
]);
}
public function checkDataResume($type, $statusLpj)
{
// Ambil data JSON dari statusLpj
$data = json_decode($statusLpj->resume, true) ?? [];
$validationRules = [
'resume' => [
'fisik'
],
];
// Validasi data JSON
if (isset($validationRules[$type])) {
$missingFields = [];
foreach ($validationRules[$type] as $field) {
$keys = explode('.', $field);
$value = $data;
foreach ($keys as $key) {
if (!isset($value[$key])) {
$missingFields[] = $field;
break;
}
$value = $value[$key];
}
// Validasi khusus untuk array fisik dan sesuai_imb
if ($field === 'fisik' || $field === 'sesuai_imb') {
if (empty($value) || !is_array($value)) {
$missingFields[] = $field;
continue;
}
// Validasi struktur data di dalam array
foreach ($value as $item) {
$requiredKeys = ['sertifikat', 'luas_tanah', 'nilai'];
foreach ($requiredKeys as $requiredKey) {
if (!isset($item[$requiredKey])) {
$missingFields[] = $field . '.' . $requiredKey;
}
}
}
}
}
// Jika ada field yang kosong, kembalikan error
if (!empty($missingFields)) {
return response()->json([
'status' => null,
'message' => "Silahkan lengkapi data resume terlebih dahulu.",
'missing_fields' => $missingFields,
], 400);
}
}
// Jika data valid
return response()->json([
'status' => $type,
'message' => "Data resume valid.",
]);
}
public function checkDataLpj($type, $statusLpj)
{
// Ambil data JSON dari statusLpj
$data = json_decode($statusLpj->lpj, true) ?? [];
$validationRules = [
'lpj' => [
'luas_tanah',
'nilai_tanah_1',
'nilai_tanah_2',
'luas_bangunan',
'nilai_bangunan_1',
'nilai_bangunan_2',
'total_nilai_pasar_wajar',
'likuidasi',
'likuidasi_nilai_1',
'likuidasi_nilai_2',
'asuransi_luas_bangunan',
'asuransi_nilai_1',
'asuransi_nilai_2',
'npw_tambahan'
],
];
// Validasi data JSON
if (isset($validationRules[$type])) {
$missingFields = [];
foreach ($validationRules[$type] as $field) {
// Penanganan khusus untuk field yang boleh null
if (in_array($field, ['sarana_pelengkap_penilai', 'nilai_sarana_pelengkap_1', 'nilai_sarana_pelengkap_2'])) {
continue;
}
if (!isset($data[$field])) {
$missingFields[] = $field;
continue;
}
// Validasi khusus untuk npw_tambahan
if ($field === 'npw_tambahan' && is_array($data[$field])) {
foreach ($data[$field] as $index => $item) {
$requiredKeys = ['name', 'luas', 'nilai_1', 'nilai_2'];
foreach ($requiredKeys as $key) {
if (!isset($item[$key])) {
$missingFields[] = "npw_tambahan[$index].$key";
}
}
}
}
}
// Jika ada field yang kosong, kembalikan error
if (!empty($missingFields)) {
return response()->json([
'status' => null,
'message' => "Silahkan lengkapi data LPJ terlebih dahulu.",
'missing_fields' => $missingFields,
], 400);
}
}
// Jika data valid
return response()->json([
'status' => $type,
'message' => "Data LPJ valid.",
]);
}
public function checkDataRap($type, $statusLpj)
{
// Ambil data JSON dari statusLpj
$data = json_decode($statusLpj->rap, true) ?? [];
$requiredFields = [
'dari',
'kepada',
'perihal',
'tanggal',
'nomor_rap'
];
// Cek apakah ada field yang kosong
$missingFields = [];
foreach ($requiredFields as $field) {
if (!isset($data[$field]) || empty($data[$field])) {
$missingFields[] = $field;
}
}
// Jika ada field yang kosong, kembalikan error
if (!empty($missingFields)) {
return response()->json([
'status' => null,
'message' => "Silahkan lengkapi data RAP terlebih dahulu.",
'missing_fields' => $missingFields
], 400);
}
// Jika semua data terisi
return response()->json([
'status' => $type,
'message' => "Data RAP valid."
]);
}
public function revisiSurveyor(Request $request, $id)
{
$permohonan = Permohonan::findOrFail($id);

View File

@@ -517,18 +517,12 @@ class PenilaianController extends Controller
$role = Auth::user()->roles[0]->name;
$status = 'done';
$approvalField = null;
$lpj_ = optional(json_decode($permohonan->penilai->lpj));
$npw = $lpj_->total_nilai_pasar_wajar ?? 0;
$npw = str_replace('.', '', $npw);
if ($role === 'senior-officer') {
$approvalField = 'approval_so';
$status = $npw <=1000000000 ? 'done' : 'proses-laporan';
$status = in_array($permohonan->nilai_plafond_id, [3]) ? 'done' : 'proses-laporan';
} elseif ($role === 'EO Appraisal') {
$approvalField = 'approval_eo';
$status = $npw <=5000000000 ? 'done' : 'proses-laporan';
$status = in_array($permohonan->nilai_plafond_id, [2, 1]) ? 'done' : 'proses-laporan';
} elseif ($role === 'DD Appraisal') {
$approvalField = 'approval_dd';
$status = 'done';

View File

@@ -23,13 +23,9 @@
use Modules\Lpj\Models\Penilaian;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\PermohonanPembatalan;
use Modules\Lpj\Models\PersetujuanPenawaran;
use Modules\Lpj\Models\StatusPermohonan;
use Modules\Lpj\Models\TujuanPenilaian;
use Modules\Lpj\Services\PermohonanHistoryService;
use Modules\Lpj\Models\Noc;
class PermohonanController extends Controller
{
@@ -50,9 +46,6 @@
{
$validate = $request->validated();
if ($validate) {
if(auth()->user()->hasRole('admin')){
$validate['status'] = "preregister";
}
try {
// Process file upload
$filePath = null;
@@ -175,7 +168,7 @@
// Retrieve data from the database
$query = Permohonan::query();
if (!Auth::user()->hasAnyRole(['administrator','admin'])) {
if (!Auth::user()->hasAnyRole(['administrator'])) {
$query = $query->where('branch_id', Auth::user()->branch_id);
}
@@ -365,55 +358,6 @@
$permohonan->status = $request->status;
$permohonan->keterangan = $request->keterangan;
$permohonan->save();
if ($permohonan->status_bayar == 'belum_bayar') {
PersetujuanPenawaran::firstOrCreate(
['permohonan_id' => $id],
['created_by' => Auth::id()]
);
}
if ($permohonan->status == 'sudah_dibayar') {
$documents = $permohonan->dokumenjaminan->first(function ($doc) {
return $doc->detail && $doc->detail->contains('name', 'Bukti Bayar');
});
$buktiBayar = $documents->detail->filter(function ($detail) {
return $detail->name == 'Bukti Bayar';
})->first() ?? null;
if ($buktiBayar->isEmpty()) {
return redirect()->route('authorization.show', $id)->with('error', 'Bukti Bayar harus diunggah');
}
$dokumenJaminan = json_decode($buktiBayar->dokumen_jaminan);
$persetujuanPenawaran = PersetujuanPenawaran::firstOrCreate(
['permohonan_id' => $id],
[
'created_by' => Auth::id(),
'bukti_bayar' => $buktiBayar->first()->dokumen_jaminan[0],
]
);
try {
Noc::updateOrCreate([
'permohonan_id' => $persetujuanPenawaran->permohonan_id,
'persetujuan_penawaran_id' => $persetujuanPenawaran->id
],[
'bukti_bayar' => $persetujuanPenawaran->bukti_bayar,
]);
} catch (\Exception $e) {
\Log::error('Failed to create or update NOC: ' . $e->getMessage());
return redirect()
->route('persetujuan-penawaran.index')
->with('error', 'Persetujuan Penawaran berhasil disimpan tetapi gagal membuat NOC: ' . $e->getMessage());
}
}
} catch (Exception $e) {
return redirect()->route('authorization.show', $id)->with('error', 'Failed to update permohonan');
}

View File

@@ -40,7 +40,6 @@
$validated = $request->validated();
$validated['created_by'] = Auth::id();
$validated['status'] = '0';
$validated['nominal_bayar'] = $validated['biaya_final'];
$persetujuanPenawaran = PersetujuanPenawaran::updateOrCreate(
['penawaran_id' => $validated['penawaran_id']],
@@ -180,8 +179,8 @@
public function edit($id)
{
$permohonan = Permohonan::with(['debiture', 'penawaranTender.detail'])->find($id);
$persetujuanPenawaran = PersetujuanPenawaran::where('permohonan_id', $id)->first();
return view('lpj::persetujuan_penawaran.form', compact('permohonan', 'persetujuanPenawaran'));
return view('lpj::persetujuan_penawaran.form', compact('permohonan'));
}
/**
@@ -202,12 +201,8 @@
}
// Retrieve data from the database
//$query = Permohonan::query()->where(['status' => 'persetujuan-penawaran']);
$query = Permohonan::query()
->where(['status' => 'persetujuan-penawaran'])
->whereHas('penawaranTender', function ($q) {
$q->where('status', 'persetujuan-penawaran');
});
$query = Permohonan::query()->where(['status' => 'persetujuan-penawaran']);
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');

View File

@@ -49,7 +49,7 @@
$query =PenawaranTender::query()
->select('penawaran.*', 'tujuan_penilaian_kjpp.name as tujuan_penilaian_kjpp_name')
->leftJoin('tujuan_penilaian_kjpp', 'tujuan_penilaian_kjpp.id','=','penawaran.tujuan_penilaian_kjpp_id')
->where('penawaran.status','=','registrasi-final')
->where('penawaran.status','=','spk')
->withCount('penawarandetails');
// Apply search filter if provided

View File

@@ -1,548 +0,0 @@
<?php
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Lpj\Exports\SlikExport;
use Modules\Lpj\Imports\SlikImport;
use Modules\Lpj\Models\Slik;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
/**
* Controller untuk mengelola data Slik
*
* Menangani operasi CRUD dan import data Slik dari file Excel
* dengan fitur server-side processing untuk datatables
*
* @package Modules\Lpj\Http\Controllers
*/
class SlikController extends Controller
{
public $user;
/**
* Constructor
*/
public function __construct()
{
$this->user = Auth::user();
}
/**
* Menampilkan halaman index slik
*
* @return \Illuminate\View\View
*/
public function index()
{
return view('lpj::slik.index');
}
/**
* Menampilkan detail slik
*
* @param int $id
* @return \Illuminate\View\View
*/
public function show($id)
{
$slik = Slik::findOrFail($id);
return view('lpj::slik.show', compact('slik'));
}
/**
* Data untuk datatables dengan server-side processing
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function dataForDatatables(Request $request)
{
// Authorization check dapat ditambahkan sesuai kebutuhan
// if (is_null($this->user)) {
// abort(403, 'Unauthorized access.');
// }
// Retrieve data from the database
$query = Slik::query();
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('sandi_bank', 'LIKE', "%$search%")
->orWhere('no_rekening', 'LIKE', "%$search%")
->orWhere('cif', 'LIKE', "%$search%")
->orWhere('nama_debitur', 'LIKE', "%$search%")
->orWhere('nama_cabang', 'LIKE', "%$search%")
->orWhere('jenis_agunan', 'LIKE', "%$search%")
->orWhere('nama_pemilik_agunan', 'LIKE', "%$search%")
->orWhere('alamat_agunan', 'LIKE', "%$search%")
->orWhere('lokasi_agunan', 'LIKE', "%$search%");
});
}
// Apply year filter
if ($request->has('year') && !empty($request->get('year'))) {
$query->byYear($request->get('year'));
}
// Apply month filter
if ($request->has('month') && !empty($request->get('month'))) {
$query->byMonth($request->get('month'));
}
// Apply sandi bank filter
if ($request->has('sandi_bank') && !empty($request->get('sandi_bank'))) {
$query->where('sandi_bank', $request->get('sandi_bank'));
}
// Apply kolektibilitas filter
if ($request->has('kolektibilitas') && !empty($request->get('kolektibilitas'))) {
$query->where('kolektibilitas', $request->get('kolektibilitas'));
}
// Apply jenis agunan filter
if ($request->has('jenis_agunan') && !empty($request->get('jenis_agunan'))) {
$query->where('jenis_agunan', $request->get('jenis_agunan'));
}
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField', 'created_at');
$query->orderBy($column, $order);
} else {
$query->orderBy('created_at', 'desc');
}
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the filtered count of records
$filteredRecords = $query->count();
// Get the data for the current page with relationships
$data = $query->get();
// Transform data untuk datatables
$transformedData = $data->map(function ($item) {
return [
'id' => $item->id,
'sandi_bank' => $item->sandi_bank,
'tahun' => $item->tahun,
'bulan' => $item->bulan,
'no_rekening' => $item->no_rekening,
'cif' => $item->cif,
'nama_debitur' => $item->nama_debitur,
'kolektibilitas' => $item->kolektibilitas,
'kolektibilitas_badge' => $item->kolektibilitas_badge,
'fasilitas' => $item->fasilitas,
'jenis_agunan' => $item->jenis_agunan,
'nama_pemilik_agunan' => $item->nama_pemilik_agunan,
'nilai_agunan' => $item->nilai_agunan_formatted,
'nilai_agunan_ljk' => $item->nilai_agunan_ljk_formatted,
'alamat_agunan' => $item->alamat_agunan,
'lokasi_agunan' => $item->lokasi_agunan,
'nama_cabang' => $item->nama_cabang,
'kode_cabang' => $item->kode_cabang,
'created_by' => $item->creator?->name ?? '-',
'created_at' => dateFormat($item->created_at, true)
];
});
// Calculate the page count
$pageCount = ceil($totalRecords / ($request->get('size', 10)));
// Calculate the current page number
$currentPage = $request->get('page', 1);
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $totalRecords,
'data' => $transformedData,
]);
}
/**
* Import data slik dari Excel dengan optimasi memory dan progress tracking
*
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function import(Request $request)
{
Log::info('SlikController: Starting import process with optimizations', [
'user_id' => Auth::id(),
'request_size' => $request->header('Content-Length'),
'has_file' => $request->hasFile('file'),
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time')
]);
// Validasi file upload dengan logging detail dan error handling komprehensif
try {
// Cek apakah ada file yang diupload
if (!$request->hasFile('file')) {
Log::error('SlikController: Tidak ada file yang diupload', [
'user_id' => Auth::id(),
'files_count' => count($request->allFiles()),
'request_data' => $request->all()
]);
throw ValidationException::withMessages(['file' => 'Tidak ada file yang diupload.']);
}
$file = $request->file('file');
// Cek apakah file valid
if (!$file->isValid()) {
$error = $file->getError();
$errorMessage = match($error) {
UPLOAD_ERR_INI_SIZE => 'File terlalu besar (melebihi upload_max_filesize).',
UPLOAD_ERR_FORM_SIZE => 'File terlalu besar (melebihi MAX_FILE_SIZE).',
UPLOAD_ERR_PARTIAL => 'File hanya terupload sebagian.',
UPLOAD_ERR_NO_FILE => 'Tidak ada file yang diupload.',
UPLOAD_ERR_NO_TMP_DIR => 'Direktori temp tidak tersedia.',
UPLOAD_ERR_CANT_WRITE => 'Gagal menulis file ke disk.',
UPLOAD_ERR_EXTENSION => 'Upload dibatalkan oleh ekstensi PHP.',
default => 'Error upload tidak diketahui: ' . $error
};
Log::error('SlikController: File upload tidak valid', [
'error' => $error,
'error_message' => $errorMessage,
'user_id' => Auth::id(),
'file_info' => [
'name' => $file->getClientOriginalName(),
'size' => $file->getSize(),
'mime' => $file->getMimeType()
]
]);
throw ValidationException::withMessages(['file' => $errorMessage]);
}
$maxFileSize = config('import.slik.max_file_size', 50) * 1024; // dalam KB
$request->validate([
'file' => 'required|file|mimes:xlsx,xls|max:' . $maxFileSize
]);
Log::info('SlikController: Validasi file berhasil');
} catch (\Illuminate\Validation\ValidationException $e) {
Log::error('SlikController: Validasi file gagal', [
'errors' => $e->errors(),
'user_id' => Auth::id(),
'request_size' => $request->header('Content-Length')
]);
throw $e;
}
try {
$uploadedFile = $request->file('file');
$originalName = $uploadedFile->getClientOriginalName();
$fileSize = $uploadedFile->getSize();
Log::info('SlikController: Memulai import data Slik', [
'user_id' => Auth::id(),
'filename' => $originalName,
'filesize' => $fileSize,
'filesize_mb' => round($fileSize / 1024 / 1024, 2),
'mime_type' => $uploadedFile->getMimeType(),
'extension' => $uploadedFile->getClientOriginalExtension()
]);
// Generate unique import ID
$importId = uniqid('slik_import_');
$userId = Auth::id() ?? 1;
// Cek apakah menggunakan queue processing untuk file besar
$useQueue = config('import.slik.queue.enabled', false) && $fileSize > (5 * 1024 * 1024); // > 5MB
// Pastikan direktori temp ada
$tempDir = storage_path('app/temp');
if (!file_exists($tempDir)) {
mkdir($tempDir, 0755, true);
Log::info('SlikController: Direktori temp dibuat', ['path' => $tempDir]);
}
// Simpan file sementara dengan nama unik
$tempFileName = 'slik_import_' . time() . '_' . uniqid() . '.' . $uploadedFile->getClientOriginalExtension();
$tempFilePath = $tempDir . '/' . $tempFileName;
Log::info('SlikController: Memindahkan file ke temp', [
'temp_path' => $tempFilePath,
'use_queue' => $useQueue
]);
// Pindahkan file ke direktori temp
$uploadedFile->move($tempDir, $tempFilePath);
// Verifikasi file berhasil dipindahkan
if (!file_exists($tempFilePath)) {
throw new Exception('File gagal dipindahkan ke direktori temp');
}
Log::info('SlikController: File berhasil dipindahkan', [
'file_size' => filesize($tempFilePath)
]);
if ($useQueue) {
Log::info('SlikController: Menggunakan queue processing untuk file besar', [
'import_id' => $importId,
'file_size_mb' => round($fileSize / 1024 / 1024, 2)
]);
// Dispatch job ke queue
\Modules\Lpj\Jobs\ProcessSlikImport::dispatch($tempFilePath, $userId, $importId);
return redirect()->back()->with('success', 'Import sedang diproses di background. ID: ' . $importId);
}
// Import langsung untuk file kecil
Log::info('SlikController: Processing file directly', [
'import_id' => $importId,
'file_size_mb' => round($fileSize / 1024 / 1024, 2)
]);
// Set optimasi memory untuk import langsung
$memoryLimit = config('import.slik.memory_limit', 256);
ini_set('memory_limit', $memoryLimit . 'M');
ini_set('max_execution_time', config('import.slik.timeout', 30000));
// Enable garbage collection jika diizinkan
if (config('import.slik.enable_gc', true)) {
gc_enable();
}
// Proses import menggunakan SlikImport class
Log::info('SlikController: Memulai proses Excel import');
$import = new SlikImport();
Excel::import($import, $tempFilePath);
Log::info('SlikController: Excel import selesai');
// Force garbage collection setelah selesai
if (config('import.slik.enable_gc', true)) {
gc_collect_cycles();
}
// Hapus file temporary setelah import
if (file_exists($tempFilePath)) {
unlink($tempFilePath);
Log::info('SlikController: File temp berhasil dihapus');
}
Log::info('SlikController: Data Slik berhasil diimport', [
'user_id' => Auth::id(),
'import_id' => $importId
]);
return redirect()->back()->with('success', 'Data Slik berhasil diimport dari file Excel.');
} catch (Exception $e) {
// Hapus file temporary jika ada error
if (isset($tempFilePath) && file_exists($tempFilePath)) {
unlink($tempFilePath);
Log::info('SlikController: File temp dihapus karena error');
}
Log::error('SlikController: Gagal import data Slik', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'user_id' => Auth::id(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'memory_usage' => memory_get_usage(true)
]);
return redirect()->back()->with('error', 'Gagal import data Slik: ' . $e->getMessage());
}
}
/**
* Menampilkan halaman form import
*
* @return \Illuminate\View\View
*/
public function importForm()
{
return view('lpj::slik.import');
}
/**
* Download template Excel untuk import
*
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function downloadTemplate()
{
$templatePath = resource_path('metronic/slik.xlsx');
if (!file_exists($templatePath)) {
return redirect()->back()->with('error', 'Template file tidak ditemukan.');
}
return response()->download($templatePath, 'template_slik.xlsx');
}
/**
* Get import progress
*
* @param string $importId
* @return \Illuminate\Http\JsonResponse
*/
public function progress(string $importId)
{
try {
$progressService = new \Modules\Lpj\Services\ImportProgressService();
$progress = $progressService->getProgress($importId);
if (!$progress) {
return response()->json([
'success' => false,
'message' => 'Progress import tidak ditemukan'
], 404);
}
return response()->json([
'success' => true,
'progress' => $progress
]);
} catch (\Exception $e) {
Log::error('SlikController: Error getting progress', [
'import_id' => $importId,
'error' => $e->getMessage()
]);
return response()->json([
'success' => false,
'message' => 'Gagal mendapatkan progress: ' . $e->getMessage()
], 500);
}
}
/**
* Export data SLIK ke Excel
*
* Method ini menangani export data SLIK ke format Excel dengan:
* - Logging aktivitas export
* - Error handling yang proper
* - Format Excel yang sesuai dengan struktur SLIK
*
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function export()
{
try {
Log::info('SLIK Export: Memulai proses export data SLIK', [
'user_id' => Auth::id(),
'timestamp' => now(),
'memory_usage' => memory_get_usage(true) / 1024 / 1024 . ' MB'
]);
// Hitung total data yang akan di-export
$totalData = Slik::count();
Log::info('SLIK Export: Informasi data export', [
'total_records' => $totalData,
'user_id' => Auth::id(),
'timestamp' => now()
]);
// Generate nama file dengan timestamp
$filename = 'slik_export_' . date('Y-m-d_H-i-s') . '.xlsx';
// Proses export menggunakan SlikExport class
$export = Excel::download(new SlikExport(), $filename);
Log::info('SLIK Export: Berhasil generate file export', [
'filename' => $filename,
'total_records' => $totalData,
'user_id' => Auth::id(),
'timestamp' => now(),
'memory_peak' => memory_get_peak_usage(true) / 1024 / 1024 . ' MB'
]);
return $export;
} catch (\Exception $e) {
Log::error('SLIK Export: Gagal melakukan export data SLIK', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'user_id' => Auth::id(),
'timestamp' => now(),
'memory_usage' => memory_get_usage(true) / 1024 / 1024 . ' MB'
]);
return response()->json([
'success' => false,
'message' => 'Gagal melakukan export data SLIK: ' . $e->getMessage()
], 500);
}
}
/**
* Truncate all SLIK data
*
* @return \Illuminate\Http\JsonResponse
*/
public function truncate()
{
try {
DB::beginTransaction();
Log::info('SLIK Truncate: Memulai proses truncate data SLIK', [
'user_id' => Auth::id(),
'timestamp' => now()
]);
// Truncate tabel SLIK
Slik::truncate();
DB::commit();
Log::info('SLIK Truncate: Berhasil menghapus semua data SLIK', [
'user_id' => Auth::id(),
'timestamp' => now()
]);
return response()->json([
'success' => true,
'message' => 'Semua data SLIK berhasil dihapus'
]);
} catch (\Exception $e) {
DB::rollback();
Log::error('SLIK Truncate: Gagal menghapus data SLIK', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'user_id' => Auth::id(),
'timestamp' => now()
]);
return response()->json([
'success' => false,
'message' => 'Gagal menghapus data SLIK: ' . $e->getMessage()
], 500);
}
}
}

View File

@@ -345,19 +345,6 @@ use Illuminate\Support\Facades\Auth;
$content = $pdf->download()->getOriginalContent();
Storage::put('public/'.$newFileNameWithPath,$content);
$permohonanModel = Permohonan::where('nomor_registrasi', $penawaran->nomor_registrasi)->first();
if ($permohonanModel) {
$permohonanModel->status = 'registrasi-final';
$permohonanModel->save();
}
$persetujuanPenawaran = PenawaranTender::where('id', $penawaran->id)->first();
if ($persetujuanPenawaran) {
$persetujuanPenawaran->status = 'registrasi-final';
$persetujuanPenawaran->save();
}
$data1['status'] = 'success';
$data1['spkpenawaran_path'] = $spkpenawaran_path;
$data1['message']['message_success'] = array('Generate SPK PDF successfully');

View File

@@ -3,19 +3,25 @@
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Lpj\Exports\BasicDataSurveyorExport;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Mail;
use Illuminate\Http\Response;
use Modules\Lpj\Models\Debiture;
use Modules\Lpj\Models\LaporanExternal;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\Branch;
use Modules\Lpj\Models\Surveyor;
use Modules\Lpj\Models\BentukTanah;
use Modules\Lpj\Models\KonturTanah;
use Modules\Location\Models\Province;
@@ -35,8 +41,17 @@ use Modules\Lpj\Models\SpekBangunan;
use Modules\Lpj\Models\SpekKategoritBangunan;
use Modules\Lpj\Models\SaranaPelengkap;
use Modules\Lpj\Models\ArahMataAngin;
use Modules\Lpj\Models\Analisa;
use Modules\Lpj\Models\Penilaian;
use Modules\Lpj\Models\PerkerasanJalan;
use Modules\Lpj\Models\AnalisaFakta;
use Modules\Lpj\Models\AnalisaLingkungan;
use Modules\Lpj\Models\AnalisaTanahBagunan;
use Modules\Lpj\Models\SpekBangunanAnalisa;
use Modules\Lpj\Models\Denah;
use Modules\Lpj\Models\FotoJaminan;
use Modules\Lpj\Models\Lingkungan;
use Modules\Lpj\Models\LantaiUnit;
use Modules\Lpj\Models\Teams;
use Modules\Lpj\Models\Lantai;
use Modules\Lpj\Models\Inspeksi;
@@ -47,24 +62,29 @@ use Modules\Lpj\Models\PosisiUnit;
use Modules\Lpj\Models\TerletakArea;
use Modules\Lpj\Models\FasilitasObjek;
use Modules\Lpj\Models\MerupakanDaerah;
use Modules\Lpj\Models\ObjekJaminan;
use Modules\Lpj\Models\ModelAlatBerat;
use Modules\Lpj\Models\JenisPesawat;
use Modules\Lpj\Models\DokumenJaminan;
use Modules\Lpj\Models\DetailDokumenJaminan;
use Modules\Lpj\Models\JenisKapal;
use Modules\Lpj\Models\JenisKendaraan;
use Modules\Lpj\Models\RuteJaminan;
use Modules\Lpj\Models\HubunganPemilikJaminan;
use Modules\Lpj\Models\HubunganPenghuniJaminan;
use Modules\Lpj\Models\AnalisaUnit;
use Modules\Lpj\Models\GolonganMasySekitar;
use Modules\Lpj\Models\TingkatKeramaian;
use Modules\Lpj\Models\TujuanPenilaian;
use Modules\Lpj\Models\LaluLintasLokasi;
use Modules\Lpj\Models\SpekBagunanAnalisaDetail;
use Modules\Lpj\Http\Requests\SurveyorRequest;
use Modules\Lpj\Http\Requests\FormSurveyorRequest;
use Modules\Lpj\Jobs\SendJadwalKunjunganEmailJob;
use App\Helpers\Lpj;
use Modules\Lpj\Models\Authorization;
use Modules\Lpj\Services\SurveyorValidateService;
use Modules\Lpj\Services\SaveFormInspesksiService;
use Modules\Lpj\Models\PermohonanHistory;
class SurveyorController extends Controller
{
@@ -131,17 +151,13 @@ class SurveyorController extends Controller
});
}
$catatan_revisi_survey = PermohonanHistory::where('permohonan_id', $id)
->where('status', 'revisi-survey')->latest()->first();
return view('lpj::surveyor.detail', compact(
'permohonan',
'surveyor',
'branches',
'provinces',
'bentukTanah',
'inspeksiData',
'catatan_revisi_survey',
'inspeksiData'
));
}
@@ -778,12 +794,12 @@ class SurveyorController extends Controller
]);
if (in_array(strtolower($permohonan->jenisPenilaian->name), ['external', 'eksternal'])) {
if ($permohonan->jenisPenilaian->name == "External") {
LaporanExternal::updateOrCreate(
['permohonan_id' => $permohonan->id],
[
'nomor_laporan' => $permohonan->nomor_registrasi,
'tgl_final_laporan' => now(),
'tanggal_laporan' => now(),
'created_by' => Auth::id(),
]
);
@@ -1380,11 +1396,6 @@ class SurveyorController extends Controller
}
public function storeDataPembanding(Request $request)
{
$request->merge([
'kordinat_lat' => $request->get('kordinat_lat') ? str_replace(',', '', $request->get('kordinat_lat')) : null,
'kordinat_lng' => $request->get('kordinat_lng') ? str_replace(',', '', $request->get('kordinat_lng')) : null,
]);
try {
DB::beginTransaction();
@@ -1836,10 +1847,10 @@ class SurveyorController extends Controller
return redirect()
->route('basicdata.' . $type . '.index')
->with('success', 'created successfully');
} catch (\Exception $e) {
} catch (Exeception $e) {
return redirect()
->route('basicdata.' . $type . '.index')
->with('error', $e->getMessage());
->with('error', $th->getMessage());
}
}
}
@@ -2105,7 +2116,7 @@ class SurveyorController extends Controller
public function dataForDatatablesData(Request $request, $type)
{
if (is_null(Auth::user()) || !$this->user->can('jenis_aset.view')) {
if (is_null(auth()->user()) || !$this->user->can('jenis_aset.view')) {
//abort(403, 'Sorry! You are not allowed to view users.');
}
@@ -2145,7 +2156,7 @@ class SurveyorController extends Controller
if (array_key_exists($type, $models)) {
$query = $models[$type]::query();
} else {
throw new \InvalidArgumentException("Invalid type: $type");
throw new InvalidArgumentException("Invalid type: $type");
}
if ($request->has('search') && !empty($request->get('search'))) {
@@ -2218,9 +2229,9 @@ class SurveyorController extends Controller
$model = $modelClass::findOrFail($id);
$model->delete();
return response()->json(['success' => true, 'message' => 'deleted successfully']);
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
} catch (ModelNotFoundException $e) {
return response()->json(['success' => false, 'message' => 'not found.'], 404);
} catch (\Exception $e) {
} catch (Exception $e) {
return response()->json(['success' => false, 'message' => 'Failed to delete.'], 500);
}
}
@@ -2376,14 +2387,14 @@ class SurveyorController extends Controller
$path = $file->storeAs("public/surveyor/{$request->type}", $fileName);
if ($path === false) {
throw new \Exception("Failed to store file for {$fileKey}");
throw new Exception("Failed to store file for {$fileKey}");
}
if (isset($data[$fileKey]) && $data[$fileKey]) {
$this->deleteFile($data[$fileKey]);
}
return str_replace('public/', '', $path);
} else {
throw new \Exception("Invalid file upload for {$fileKey}");
throw new Exception("Invalid file upload for {$fileKey}");
}
} elseif (isset($data[$fileKey]) && $data[$fileKey]) {
return $data[$fileKey];
@@ -2412,14 +2423,14 @@ class SurveyorController extends Controller
public function uploadFile($file, $type)
{
if (!$file->isValid()) {
throw new \Exception("Invalid file upload for {$type}");
throw new Exception("Invalid file upload for {$type}");
}
$fileName = time() . '_' . $file->getClientOriginalName();
$path = $file->storeAs("public/surveyor/{$type}", $fileName);
if ($path === false) {
throw new \Exception("Failed to store file for {$type}");
throw new Exception("Failed to store file for {$type}");
}
return str_replace('public/', '', $path);
@@ -2707,7 +2718,7 @@ class SurveyorController extends Controller
$fileName = 'inspeksi-' . $namaDebiture . '-data.pdf';
$pdf->set_option('isHtml5ParserEnabled', true);
$pdf->set_option('isPhpEnabled', true);
return $pdf->stream($fileName);
return $pdf->download($fileName);
}
public function approveReschedule(Request $request, $id)
@@ -2869,10 +2880,9 @@ class SurveyorController extends Controller
$inspeksi->data_form = json_encode($existingData);
$inspeksi->save();
} else {
Inspeksi::updateOrCreate([
Inspeksi::create([
'permohonan_id' => $request->input('permohonan_id'),
'dokument_id' => $request->input('document_id')
],[
'dokument_id' => $request->input('document_id'),
'data_form' => json_encode($existingData),
]);
}

View File

@@ -14,7 +14,6 @@
{
$rules = [
'name' => 'required|max:255',
'biaya' => 'nullable|numeric|min:0',
];
if ($this->method() == 'PUT') {

View File

@@ -52,7 +52,7 @@
private function getBasicInfoRules()
{
return [
'permohonan_id' => 'nullable|exists:permohonan,id',
'permohonan_id' => 'required|exists:permohonan,id',
'persetujuan_penawaran_id' => 'required|exists:persetujuan_penawaran,id',
'status' => 'nullable|boolean',
'created_by' => 'nullable|exists:users,id',
@@ -72,16 +72,10 @@
return [
'total_harus_bayar' => 'nullable|numeric|min:0',
'nominal_bayar' => 'nullable|numeric|min:0',
'total_pembukuan' => 'nullable|numeric|min:0',
'bukti_ksl' => $fileRule,
'bukti_bayar' => $fileRule,
'status_bayar' => 'nullable|boolean',
'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',
];
}
@@ -95,16 +89,12 @@
$fileRule = 'nullable|file|mimes:' . self::ALLOWED_FILE_TYPES . '|max:' . self::MAX_FILE_SIZE;
return [
'nominal_penyelesaian' => 'nullable|numeric|min:0',
'status_penyelesaian' => 'nullable|boolean',
'tanggal_penyelesaian' => 'nullable|date',
'bukti_penyelesaian' => $fileRule,
'memo_penyelesaian' => $fileRule,
'catatan_noc' => 'nullable|string',
'nomor_tiket' => 'nullable|string',
'nomor_rekening_lebih_bayar' => 'nullable|string',
'bukti_ksl_lebih_bayar' => 'nullable|string',
'bukti_ksl_kurang_bayar' => 'nullable|string'
'nominal_penyelesaian' => 'nullable|numeric|min:0',
'status_penyelesaian' => 'nullable|boolean',
'tanggal_penyelesaian' => 'nullable|date',
'bukti_penyelesaian' => $fileRule,
'memo_penyelesaian' => $fileRule,
'catatan_noc' => 'nullable|string',
];
}
@@ -130,6 +120,7 @@
public function messages()
{
return [
'permohonan_id.required' => 'ID Permohonan harus diisi',
'permohonan_id.exists' => 'ID Permohonan tidak valid',
'persetujuan_penawaran_id.required' => 'ID Persetujuan Penawaran harus diisi',
'persetujuan_penawaran_id.exists' => 'ID Persetujuan Penawaran tidak valid',
@@ -157,11 +148,6 @@
'authorized_status.boolean' => 'Status Otorisasi harus berupa boolean',
'authorized_at.date' => 'Format Tanggal 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',
];
}
}

View File

@@ -48,6 +48,7 @@
),
'tanggal_permohonan' => date('Y-m-d'),
'user_id' => auth()->user()->id,
'branch_id' => auth()->user()->branch_id,
'status' => 'order'
]);
}

View File

@@ -14,54 +14,49 @@
public function rules()
{
return [
'permohonan_id' => 'nullable|exists:permohonan,id',
'penawaran_id' => 'nullable|exists:penawaran,id',
'nomor_proposal_penawaran' => 'nullable|string|max:255',
'nomor_tiket' => 'nullable|string|max:100',
'nominal_kurang_bayar' => 'nullable|string|max:100',
'bukti_ksl_kurang_bayar' => 'nullable|file|mimes:pdf,doc,docx|max:10240',
'tanggal_proposal_penawaran' => 'nullable|date',
'biaya_final' => 'nullable|numeric|min:0',
'sla_resume' => 'nullable|numeric|min:0',
'sla_final' => 'nullable|numeric|min:0',
'file_persetujuan_penawaran' => 'nullable|file|mimes:pdf,doc,docx|max:10240',
'surat_representasi' => 'nullable|file|mimes:pdf,doc,docx|max:10240',
'status' => 'nullable|boolean',
'authorized_status' => 'boolean',
'authorized_at' => 'nullable|date',
'authorized_by' => 'nullable|exists:users,id',
'catatan' => 'nullable|string',
];
'permohonan_id' => 'nullable|exists:permohonan,id',
'penawaran_id' => 'nullable|exists:penawaran,id',
'nomor_proposal_penawaran' => 'nullable|string|max:255',
'tanggal_proposal_penawaran' => 'nullable|date',
'biaya_final' => 'nullable|numeric|min:0',
'sla_resume' => 'nullable|numeric|min:0',
'sla_final' => 'nullable|numeric|min:0',
'file_persetujuan_penawaran' => 'nullable|file|mimes:pdf,doc,docx|max:10240',
'surat_representasi' => 'nullable|file|mimes:pdf,doc,docx|max:10240',
'status' => 'nullable|boolean',
'authorized_status' => 'boolean',
'authorized_at' => 'nullable|date',
'authorized_by' => 'nullable|exists:users,id',
'catatan' => 'nullable|string',
];
}
public function messages()
{
return [
'penawaran_id.required' => 'Penawaran ID wajib diisi.',
'penawaran_id.exists' => 'Penawaran ID tidak valid.',
'nomor_proposal_penawaran.required' => 'Nomor proposal penawaran wajib diisi.',
'nomor_tiket.string' => 'Nomor tiket harus berupa teks.',
'nomor_tiket.max' => 'Nomor tiket tidak boleh lebih dari 100 karakter.',
'tanggal_proposal_penawaran.required' => 'Tanggal proposal penawaran wajib diisi.',
'tanggal_proposal_penawaran.date' => 'Tanggal proposal penawaran harus berupa tanggal yang valid.',
'biaya_final.required' => 'Biaya final wajib diisi.',
'biaya_final.numeric' => 'Biaya final harus berupa angka.',
'biaya_final.min' => 'Biaya final tidak boleh kurang dari 0.',
'sla_resume.required' => 'SLA Resume wajib diisi.',
'sla_final.required' => 'SLA Final wajib diisi.',
'file_persetujuan_penawaran.file' => 'File Persetujuan Penawaran harus berupa file.',
'file_persetujuan_penawaran.mimes' => 'File Persetujuan Penawaran harus berupa file PDF, DOC, atau DOCX.',
'file_persetujuan_penawaran.max' => 'Ukuran File Persetujuan Penawaran tidak boleh lebih dari 10MB.',
'surat_representasi.file' => 'Surat Representasi harus berupa file.',
'surat_representasi.mimes' => 'Surat Representasi harus berupa file PDF, DOC, atau DOCX.',
'surat_representasi.max' => 'Ukuran Surat Representasi tidak boleh lebih dari 10MB.',
'region_id.required' => 'Region ID wajib diisi.',
'region_id.exists' => 'Region ID tidak valid.',
'status.required' => 'Status wajib diisi.',
'status.boolean' => 'Status harus berupa nilai boolean.',
'authorized_status.boolean' => 'Status otorisasi harus berupa nilai boolean.',
'authorized_at.date' => 'Tanggal otorisasi harus berupa tanggal yang valid.',
'authorized_by.exists' => 'ID pengguna yang mengotorisasi tidak valid.',
];
'penawaran_id.required' => 'Penawaran ID wajib diisi.',
'penawaran_id.exists' => 'Penawaran ID tidak valid.',
'nomor_proposal_penawaran.required' => 'Nomor proposal penawaran wajib diisi.',
'tanggal_proposal_penawaran.required' => 'Tanggal proposal penawaran wajib diisi.',
'tanggal_proposal_penawaran.date' => 'Tanggal proposal penawaran harus berupa tanggal yang valid.',
'biaya_final.required' => 'Biaya final wajib diisi.',
'biaya_final.numeric' => 'Biaya final harus berupa angka.',
'biaya_final.min' => 'Biaya final tidak boleh kurang dari 0.',
'sla_resume.required' => 'SLA Resume wajib diisi.',
'sla_final.required' => 'SLA Final wajib diisi.',
'file_persetujuan_penawaran.file' => 'File Persetujuan Penawaran harus berupa file.',
'file_persetujuan_penawaran.mimes' => 'File Persetujuan Penawaran harus berupa file PDF, DOC, atau DOCX.',
'file_persetujuan_penawaran.max' => 'Ukuran File Persetujuan Penawaran tidak boleh lebih dari 10MB.',
'surat_representasi.file' => 'Surat Representasi harus berupa file.',
'surat_representasi.mimes' => 'Surat Representasi harus berupa file PDF, DOC, atau DOCX.',
'surat_representasi.max' => 'Ukuran Surat Representasi tidak boleh lebih dari 10MB.',
'region_id.required' => 'Region ID wajib diisi.',
'region_id.exists' => 'Region ID tidak valid.',
'status.required' => 'Status wajib diisi.',
'status.boolean' => 'Status harus berupa nilai boolean.',
'authorized_status.boolean' => 'Status otorisasi harus berupa nilai boolean.',
'authorized_at.date' => 'Tanggal otorisasi harus berupa tanggal yang valid.',
'authorized_by.exists' => 'ID pengguna yang mengotorisasi tidak valid.',
];
}
}

View File

@@ -1,341 +0,0 @@
<?php
namespace Modules\Lpj\Imports;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithStartRow;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Modules\Lpj\Models\Bucok;
use Carbon\Carbon;
use Exception;
/**
* Kelas untuk mengimpor data Excel ke tabel bucoks
* Menggunakan Laravel Excel dengan validasi dan batch processing
* Data dimulai dari baris ke-5 tanpa header
*/
class BucokImport implements ToCollection, WithStartRow, WithValidation, WithBatchInserts, WithChunkReading
{
private $importedCount = 0;
private $skippedCount = 0;
private $createdCount = 0;
private $updatedCount = 0;
private $errors = [];
/**
* Menentukan baris mulai membaca data (baris ke-5)
*
* @return int
*/
public function startRow(): int
{
return 5;
}
/**
* Memproses koleksi data dari Excel
*
* @param Collection $collection
* @return void
*/
public function collection(Collection $collection)
{
DB::beginTransaction();
try {
foreach ($collection as $rowIndex => $row) {
// Log setiap baris yang diproses
Log::info('Processing Bucok import row', [
'row_number' => $rowIndex + 5, // +5 karena mulai dari baris 5
'row_data' => $row->toArray()
]);
// Konversi row ke array dengan indeks numerik
$rowArray = $row->toArray();
// Skip baris kosong
if (empty(array_filter($rowArray))) {
continue;
}
// Validasi data baris
$mappedData = $this->mapRowToBucok($rowArray, $rowIndex + 5);
// Update atau create berdasarkan nomor_tiket
if (!empty($mappedData['nomor_tiket'])) {
// Update atau create berdasarkan nomor_tiket
$bucok = Bucok::updateOrCreate(
['nomor_tiket' => $mappedData['nomor_tiket']], // Kondisi pencarian
array_merge($mappedData, ['updated_by' => auth()->id()]) // Data yang akan diupdate/create
);
// Log dan tracking apakah data di-update atau di-create
if ($bucok->wasRecentlyCreated) {
$this->createdCount++;
Log::info('Bucok created successfully', [
'row_number' => $rowIndex + 5,
'nomor_tiket' => $mappedData['nomor_tiket'],
'action' => 'created'
]);
} else {
$this->updatedCount++;
Log::info('Bucok updated successfully', [
'row_number' => $rowIndex + 5,
'nomor_tiket' => $mappedData['nomor_tiket'],
'action' => 'updated'
]);
}
}
$this->importedCount++;
}
DB::commit();
// Log summary
Log::info('Bucok import completed', [
'imported' => $this->importedCount,
'skipped' => $this->skippedCount,
'total_errors' => count($this->errors)
]);
} catch (Exception $e) {
DB::rollback();
Log::error('Bucok import failed', ['error' => $e->getMessage()]);
throw $e;
}
}
/**
* Mapping data Excel berdasarkan indeks kolom ke struktur model Bucok
* Kolom dimulai dari indeks 0 (A=0, B=1, C=2, dst.)
*
* @param array $row
* @param int $rowNumber
* @return array
*/
private function mapRowToBucok(array $row, int $rowNumber): array
{
return [
'no' => $row[0] ?? null, // Kolom A
'tanggal' => !empty($row[1]) ? $this->parseDate($row[1]) : null, // Kolom B
'bulan' => $row[2] ?? null, // Kolom C
'tahun' => $row[3] ?? null, // Kolom D
'tanggal_penuh' => !empty($row[4]) ? $this->parseDate($row[4]) : null, // Kolom E
'nomor_categ' => $row[5] ?? null, // Kolom F
'coa_summary' => $row[6] ?? null, // Kolom G
'nomor_coa' => $row[7] ?? null, // Kolom H
'nama_coa' => $row[8] ?? null, // Kolom I
'nomor_tiket' => $row[9] ?? null, // Kolom J - Auto-generate jika kosong
'deskripsi' => $row[10] ?? null, // Kolom K
'nominal' => $this->parseNumeric($row[11] ?? 0), // Kolom L
'penyelesaian' => $row[12] ?? 'Belum Selesai', // Kolom M
'umur_aging' => $this->parseNumeric($row[13] ?? 0), // Kolom N
'cost_center' => $row[14] ?? null, // Kolom O
'nama_sub_direktorat' => $row[15] ?? null, // Kolom P
'nama_direktorat_cabang' => $row[16] ?? null, // Kolom Q
'tanggal_penyelesaian' => !empty($row[17]) ? $this->parseDate($row[17]) : null, // Kolom R
'nominal_penyelesaian' => $this->parseNumeric($row[18] ?? 0), // Kolom S
'nominal_berjalan' => $this->parseNumeric($row[19] ?? 0), // Kolom T
'amortisasi_berjalan' => $this->parseNumeric($row[20] ?? 0), // Kolom U
'sistem_berjalan' => $this->parseNumeric($row[21] ?? 0), // Kolom V
'lainnya_berjalan' => $this->parseNumeric($row[22] ?? 0), // Kolom W
'nominal_gantung' => $this->parseNumeric($row[23] ?? 0), // Kolom X
'aset_gantung' => $this->parseNumeric($row[24] ?? 0), // Kolom Y
'keterangan_gantung' => $row[25] ?? null, // Kolom Z
'lainnya_satu' => $row[26] ?? null, // Kolom AA
'lainnya_dua' => $row[27] ?? null, // Kolom AB
'created_by' => auth()->id(),
'updated_by' => auth()->id()
];
}
/**
* Parse tanggal dari berbagai format
*
* @param mixed $dateValue
* @return Carbon|null
*/
private function parseDate($dateValue)
{
if (empty($dateValue)) {
return null;
}
try {
// Jika berupa angka Excel date serial
if (is_numeric($dateValue)) {
return Carbon::createFromFormat('Y-m-d', \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($dateValue)->format('Y-m-d'));
}
// Jika berupa string tanggal
return Carbon::parse($dateValue);
} catch (Exception $e) {
Log::warning('Failed to parse date', ['value' => $dateValue, 'error' => $e->getMessage()]);
return null;
}
}
/**
* Parse nilai numerik dari berbagai format
*
* @param mixed $numericValue
* @return float
*/
private function parseNumeric($numericValue): float
{
if (empty($numericValue)) {
return 0;
}
// Hapus karakter non-numerik kecuali titik dan koma
$cleaned = preg_replace('/[^0-9.,\-]/', '', $numericValue);
// Ganti koma dengan titik untuk decimal
$cleaned = str_replace(',', '.', $cleaned);
return (float) $cleaned;
}
/**
* Validasi data yang sudah dimapping
*
* @param array $data
* @return \Illuminate\Validation\Validator
*/
private function validateMappedData(array $data)
{
return Validator::make($data, [
'no' => 'nullable|integer',
'tanggal' => 'nullable|date',
'bulan' => 'nullable|integer|between:1,12',
'tahun' => 'nullable|integer|min:2000|max:2099',
'tanggal_penuh' => 'nullable|date',
'nomor_categ' => 'nullable|string|max:50',
'coa_summary' => 'nullable|string|max:255',
'nomor_coa' => 'nullable|string|max:50',
'nama_coa' => 'nullable|string|max:255',
'nomor_tiket' => 'nullable|string|max:50',
'deskripsi' => 'nullable|string',
'nominal' => 'nullable|numeric|min:0',
'penyelesaian' => 'nullable|in:Selesai,Belum Selesai,Dalam Proses',
'umur_aging' => 'nullable|integer|min:0',
'cost_center' => 'nullable|string|max:100',
'nama_sub_direktorat' => 'nullable|string|max:255',
'nama_direktorat_cabang' => 'nullable|string|max:255',
'tanggal_penyelesaian' => 'nullable|date',
'nominal_penyelesaian' => 'nullable|numeric|min:0',
'nominal_berjalan' => 'nullable|numeric|min:0',
'amortisasi_berjalan' => 'nullable|numeric|min:0',
'sistem_berjalan' => 'nullable|numeric|min:0',
'lainnya_berjalan' => 'nullable|numeric|min:0',
'nominal_gantung' => 'nullable|numeric|min:0',
'aset_gantung' => 'nullable|numeric|min:0',
'keterangan_gantung' => 'nullable|string',
'lainnya_satu' => 'nullable|string',
'lainnya_dua' => 'nullable|string'
]);
}
/**
* Aturan validasi untuk seluruh file Excel (tidak digunakan karena tanpa header)
*
* @return array
*/
public function rules(): array
{
return [];
}
/**
* Ukuran batch untuk insert
*
* @return int
*/
public function batchSize(): int
{
return 100;
}
/**
* Ukuran chunk untuk membaca file
*
* @return int
*/
public function chunkSize(): int
{
return 100;
}
/**
* Mendapatkan jumlah data yang berhasil diimpor
*
* @return int
*/
public function getImportedCount(): int
{
return $this->importedCount;
}
/**
* Mendapatkan jumlah data yang berhasil dibuat
*
* @return int
*/
public function getCreatedCount(): int
{
return $this->createdCount;
}
/**
* Mendapatkan jumlah data yang berhasil diupdate
*
* @return int
*/
public function getUpdatedCount(): int
{
return $this->updatedCount;
}
/**
* Mendapatkan statistik lengkap import
*
* @return array
*/
public function getImportStatistics(): array
{
return [
'total_processed' => $this->importedCount,
'created' => $this->createdCount,
'updated' => $this->updatedCount,
'skipped' => $this->skippedCount,
'errors' => count($this->errors)
];
}
/**
* Mendapatkan jumlah data yang dilewati
*
* @return int
*/
public function getSkippedCount(): int
{
return $this->skippedCount;
}
/**
* Mendapatkan daftar error yang terjadi
*
* @return array
*/
public function getErrors(): array
{
return $this->errors;
}
}

View File

@@ -1,415 +0,0 @@
<?php
namespace Modules\Lpj\Imports;
use Modules\Lpj\Models\Slik;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithStartRow;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
/**
* Class SlikImport
*
* Handle import data Excel untuk modul Slik
* Menggunakan Laravel Excel package untuk membaca file Excel
* dengan optimasi memory dan chunk processing
*
* @package Modules\Lpj\app\Imports
*/
class SlikImport implements ToCollection, WithStartRow, WithBatchInserts, WithChunkReading, WithCustomCsvSettings
{
/**
* Mulai membaca dari baris ke-5 (skip header)
*
* @return int
*/
public function startRow(): int
{
return 5;
}
/**
* Batch size untuk insert data dari konfigurasi
*
* @return int
*/
public function batchSize(): int
{
return config('import.slik.batch_size', 50);
}
/**
* Chunk size untuk membaca file dari konfigurasi
*
* @return int
*/
public function chunkSize(): int
{
return config('import.slik.chunk_size', 50);
}
/**
* Custom CSV settings untuk optimasi pembacaan file
*
* @return array
*/
public function getCsvSettings(): array
{
return [
'input_encoding' => 'UTF-8',
'delimiter' => ',',
'enclosure' => '"',
'escape_character' => '\\',
];
}
/**
* Process collection data dari Excel dengan optimasi memory
*
* @param Collection $collection
* @return void
*/
public function collection(Collection $collection)
{
// Set memory limit dari konfigurasi
$memoryLimit = config('import.slik.memory_limit', 1024);
$currentMemoryLimit = ini_get('memory_limit');
if ($currentMemoryLimit !== '-1' && $this->convertToBytes($currentMemoryLimit) < $memoryLimit * 1024 * 1024) {
ini_set('memory_limit', $memoryLimit . 'M');
}
// Set timeout handler
$timeout = config('import.slik.timeout', 1800);
set_time_limit($timeout);
// Force garbage collection sebelum memulai
if (config('import.slik.enable_gc', true)) {
gc_collect_cycles();
}
Log::info('SlikImport: Memulai import data', [
'total_rows' => $collection->count(),
'memory_usage' => memory_get_usage(true),
'memory_peak' => memory_get_peak_usage(true),
'memory_limit' => ini_get('memory_limit'),
'php_version' => PHP_VERSION,
'memory_limit_before' => $currentMemoryLimit,
'config' => [
'memory_limit' => $memoryLimit,
'chunk_size' => $this->chunkSize(),
'batch_size' => $this->batchSize()
]
]);
DB::beginTransaction();
try {
$processedRows = 0;
$skippedRows = 0;
$errorRows = 0;
$totalRows = $collection->count();
foreach ($collection as $index => $row) {
// Log progress setiap 25 baris untuk chunk lebih kecil
if ($index % 25 === 0) {
Log::info('SlikImport: Processing chunk', [
'current_row' => $index + 5,
'progress' => round(($index / max($totalRows, 1)) * 100, 2) . '%',
'processed' => $processedRows,
'skipped' => $skippedRows,
'errors' => $errorRows,
'memory_usage' => memory_get_usage(true),
'memory_peak' => memory_get_peak_usage(true),
'memory_diff' => memory_get_peak_usage(true) - memory_get_usage(true)
]);
}
// Skip baris kosong
if ($this->isEmptyRow($row)) {
$skippedRows++;
Log::debug('SlikImport: Skipping empty row', ['row_number' => $index + 5]);
continue;
}
// Validasi data
if (!$this->validateRow($row)) {
$errorRows++;
Log::warning('SlikImport: Invalid row data', [
'row_number' => $index + 5,
'data' => $row->toArray()
]);
continue;
}
try {
// Map data dari Excel ke model
$slikData = $this->mapRowToSlik($row);
// Update atau create berdasarkan no_rekening dan cif
$slik = Slik::updateOrCreate(
[
'no_rekening' => $slikData['no_rekening'],
'cif' => $slikData['cif']
],
$slikData
);
$processedRows++;
// Log detail untuk baris pertama sebagai sample
if ($index === 0) {
Log::info('SlikImport: Sample data processed', [
'slik_id' => $slik->id,
'no_rekening' => $slik->no_rekening,
'cif' => $slik->cif,
'was_recently_created' => $slik->wasRecentlyCreated
]);
}
// Force garbage collection setiap 25 baris untuk mengurangi memory
if (config('import.slik.enable_gc', true) && $index > 0 && $index % 25 === 0) {
gc_collect_cycles();
}
// Unset data yang sudah tidak digunakan untuk mengurangi memory
if ($index > 0 && $index % 25 === 0) {
unset($slikData, $slik);
}
// Reset collection internal untuk mengurangi memory
if ($index > 0 && $index % 100 === 0) {
$collection = collect($collection->slice($index + 1)->values());
}
} catch (\Exception $e) {
$errorRows++;
Log::error('SlikImport: Error processing row', [
'row_number' => $index + 5,
'error' => $e->getMessage(),
'data' => $row->toArray(),
'memory_usage' => memory_get_usage(true)
]);
}
}
DB::commit();
// Force garbage collection setelah selesai
if (config('import.slik.enable_gc', true)) {
gc_collect_cycles();
}
// Cleanup variables
unset($collection);
Log::info('SlikImport: Import berhasil diselesaikan', [
'total_rows' => $totalRows,
'processed_rows' => $processedRows,
'skipped_rows' => $skippedRows,
'error_rows' => $errorRows,
'final_memory_usage' => memory_get_usage(true),
'peak_memory_usage' => memory_get_peak_usage(true),
'memory_saved' => memory_get_peak_usage(true) - memory_get_usage(true),
'memory_efficiency' => ($processedRows > 0) ? round(memory_get_peak_usage(true) / $processedRows, 2) : 0
]);
} catch (\Exception $e) {
DB::rollBack();
// Force garbage collection jika error
if (config('import.slik.enable_gc', true)) {
gc_collect_cycles();
}
$errorType = 'general';
if (str_contains(strtolower($e->getMessage()), 'memory')) {
$errorType = 'memory';
} elseif (str_contains(strtolower($e->getMessage()), 'timeout') || str_contains(strtolower($e->getMessage()), 'maximum execution time')) {
$errorType = 'timeout';
}
Log::error('SlikImport: Error during import', [
'error' => $e->getMessage(),
'error_type' => $errorType,
'exception_type' => get_class($e),
'trace' => $e->getTraceAsString(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'memory_usage' => memory_get_usage(true),
'memory_peak' => memory_get_peak_usage(true),
'memory_limit' => ini_get('memory_limit'),
'timeout_limit' => ini_get('max_execution_time'),
'is_memory_error' => str_contains(strtolower($e->getMessage()), 'memory'),
'is_timeout_error' => str_contains(strtolower($e->getMessage()), 'timeout') || str_contains(strtolower($e->getMessage()), 'maximum execution time')
]);
throw $e;
}
}
/**
* Convert memory limit string ke bytes
*
* @param string $memoryLimit
* @return int
*/
private function convertToBytes(string $memoryLimit): int
{
$memoryLimit = trim($memoryLimit);
$lastChar = strtolower(substr($memoryLimit, -1));
$number = (int) substr($memoryLimit, 0, -1);
switch ($lastChar) {
case 'g':
return $number * 1024 * 1024 * 1024;
case 'm':
return $number * 1024 * 1024;
case 'k':
return $number * 1024;
default:
return (int) $memoryLimit;
}
}
/**
* Cek apakah baris kosong
*
* @param Collection $row
* @return bool
*/
private function isEmptyRow(Collection $row): bool
{
return $row->filter(function ($value) {
return !empty(trim($value));
})->isEmpty();
}
/**
* Validasi data baris
*
* @param Collection $row
* @return bool
*/
private function validateRow(Collection $row): bool
{
// Validasi minimal: sandi_bank, no_rekening, dan cif harus ada
return !empty(trim($row[0])) && // sandi_bank
!empty(trim($row[5])) && // no_rekening
!empty(trim($row[6])); // cif
}
/**
* Map data dari baris Excel ke array untuk model Slik
*
* @param Collection $row
* @return array
*/
private function mapRowToSlik(Collection $row): array
{
return [
'sandi_bank' => trim($row[0]) ?: null,
'tahun' => $this->parseInteger($row[1]),
'bulan' => $this->parseInteger($row[2]),
'flag_detail' => trim($row[3]) ?: null,
'kode_register_agunan' => trim($row[4]) ?: null,
'no_rekening' => trim($row[5]) ?: null,
'cif' => trim($row[6]) ?: null,
'kolektibilitas' => trim($row[7]) ?: null,
'fasilitas' => trim($row[8]) ?: null,
'jenis_segmen_fasilitas' => trim($row[9]) ?: null,
'status_agunan' => trim($row[10]) ?: null,
'jenis_agunan' => trim($row[11]) ?: null,
'peringkat_agunan' => trim($row[12]) ?: null,
'lembaga_pemeringkat' => trim($row[13]) ?: null,
'jenis_pengikatan' => trim($row[14]) ?: null,
'tanggal_pengikatan' => $this->parseDate($row[15]),
'nama_pemilik_agunan' => trim($row[16]) ?: null,
'bukti_kepemilikan' => trim($row[17]) ?: null,
'alamat_agunan' => trim($row[18]) ?: null,
'lokasi_agunan' => trim($row[19]) ?: null,
'nilai_agunan' => $this->parseDecimal($row[20]),
'nilai_agunan_menurut_ljk' => $this->parseDecimal($row[21]),
'tanggal_penilaian_ljk' => $this->parseDate($row[22]),
'nilai_agunan_penilai_independen' => $this->parseDecimal($row[23]),
'nama_penilai_independen' => trim($row[24]) ?: null,
'tanggal_penilaian_penilai_independen' => $this->parseDate($row[25]),
'jumlah_hari_tunggakan' => $this->parseInteger($row[26]),
'status_paripasu' => trim($row[27]) ?: null,
'prosentase_paripasu' => $this->parseDecimal($row[28]),
'status_kredit_join' => trim($row[29]) ?: null,
'diasuransikan' => trim($row[30]) ?: null,
'keterangan' => trim($row[31]) ?: null,
'kantor_cabang' => trim($row[32]) ?: null,
'operasi_data' => trim($row[33]) ?: null,
'kode_cabang' => trim($row[34]) ?: null,
'nama_debitur' => trim($row[35]) ?: null,
'nama_cabang' => trim($row[36]) ?: null,
'flag' => trim($row[37]) ?: null,
];
}
/**
* Parse integer value
*
* @param mixed $value
* @return int|null
*/
private function parseInteger($value): ?int
{
if (empty(trim($value))) {
return null;
}
return (int) $value;
}
/**
* Parse decimal value
*
* @param mixed $value
* @return float|null
*/
private function parseDecimal($value): ?float
{
if (empty(trim($value))) {
return null;
}
// Remove currency formatting
$cleaned = str_replace([',', '.'], ['', '.'], $value);
$cleaned = preg_replace('/[^0-9.]/', '', $cleaned);
return (float) $cleaned;
}
/**
* Parse date value
*
* @param mixed $value
* @return string|null
*/
private function parseDate($value): ?string
{
if (empty(trim($value))) {
return null;
}
try {
// Try to parse various date formats
$date = \Carbon\Carbon::parse($value);
return $date->format('Y-m-d');
} catch (\Exception $e) {
Log::warning('SlikImport: Invalid date format', [
'value' => $value,
'error' => $e->getMessage()
]);
return null;
}
}
}

View File

@@ -1,179 +0,0 @@
<?php
namespace Modules\Lpj\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Modules\Lpj\Imports\SlikImport;
use Maatwebsite\Excel\Facades\Excel;
class ProcessSlikImport implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 1800; // 30 menit untuk file besar
public $tries = 5; // Tambah retry untuk file sangat besar
public $maxExceptions = 5;
public $backoff = [60, 300, 900, 1800, 3600]; // Exponential backoff dalam detik
protected string $filePath;
protected int $userId;
protected string $importId;
/**
* Create a new job instance.
*
* @param string $filePath
* @param int $userId
* @param string $importId
*/
public function __construct(string $filePath, int $userId, string $importId)
{
$this->filePath = $filePath;
$this->userId = $userId;
$this->importId = $importId;
}
/**
* Execute the job.
*
* @return void
*/
public function handle(): void
{
Log::info('ProcessSlikImport: Memulai proses import via queue', [
'file_path' => $this->filePath,
'user_id' => $this->userId,
'import_id' => $this->importId,
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time')
]);
try {
// Cek file size terlebih dahulu
$fileSize = filesize($this->filePath);
$maxFileSize = config('import.slik.max_file_size', 50) * 1024 * 1024; // Convert MB to bytes
if ($fileSize > $maxFileSize) {
throw new \Exception('File terlalu besar: ' . number_format($fileSize / 1024 / 1024, 2) . ' MB. Maksimum: ' . config('import.slik.max_file_size', 50) . ' MB');
}
// Set optimasi memory untuk queue processing
$memoryLimit = config('import.slik.memory_limit', 1024);
ini_set('memory_limit', $memoryLimit . 'M');
ini_set('max_execution_time', config('import.slik.timeout', 1800));
// Set timeout untuk XML Scanner
$xmlScannerTimeout = config('import.slik.xml_scanner.timeout', 1800);
$xmlScannerMemory = config('import.slik.xml_scanner.memory_limit', 1024);
// Enable garbage collection jika diizinkan
if (config('import.slik.enable_gc', true)) {
gc_enable();
}
// Update progress status
$this->updateProgress('processing', 0, 'Memproses file Excel...');
Log::info('SlikImport: Processing file', [
'file' => basename($this->filePath),
'file_size' => number_format(filesize($this->filePath) / 1024 / 1024, 2) . ' MB',
'memory_limit' => $memoryLimit . 'M',
'timeout' => config('import.slik.timeout', 1800),
'enable_gc' => config('import.slik.enable_gc', true),
'xml_scanner_timeout' => config('import.slik.xml_scanner.timeout', 1800),
'chunk_size' => config('import.slik.chunk_size', 50),
'batch_size' => config('import.slik.batch_size', 50),
]);
// Import file menggunakan SlikImport
$import = new SlikImport();
Excel::import($import, $this->filePath);
// Update progress selesai
$this->updateProgress('completed', 100, 'Import berhasil diselesaikan');
Log::info('ProcessSlikImport: Import berhasil diselesaikan', [
'import_id' => $this->importId,
'file_path' => $this->filePath,
'memory_usage' => memory_get_usage(true),
'memory_peak' => memory_get_peak_usage(true)
]);
// Hapus file temporary setelah selesai
if (config('import.general.cleanup_temp_files', true)) {
Storage::delete($this->filePath);
Log::info('ProcessSlikImport: File temporary dihapus', ['file_path' => $this->filePath]);
}
} catch (\Exception $e) {
// Update progress error
$this->updateProgress('failed', 0, 'Error: ' . $e->getMessage());
Log::error('ProcessSlikImport: Error saat proses import', [
'import_id' => $this->importId,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'memory_usage' => memory_get_usage(true)
]);
throw $e;
}
}
/**
* Update progress import
*
* @param string $status
* @param int $percentage
* @param string $message
* @return void
*/
private function updateProgress(string $status, int $percentage, string $message): void
{
if (config('import.slik.progress.enabled', true)) {
$cacheKey = config('import.slik.progress.cache_key', 'slik_import_progress') . '_' . $this->importId;
$cacheTtl = config('import.slik.progress.cache_ttl', 3600);
$progressData = [
'status' => $status,
'percentage' => $percentage,
'message' => $message,
'timestamp' => now(),
'user_id' => $this->userId
];
cache()->put($cacheKey, $progressData, $cacheTtl);
}
}
/**
* Handle job failure
*
* @param \Throwable $exception
* @return void
*/
public function failed(\Throwable $exception): void
{
Log::error('ProcessSlikImport: Job failed', [
'import_id' => $this->importId,
'error' => $exception->getMessage(),
'trace' => $exception->getTraceAsString()
]);
// Update progress ke failed
$this->updateProgress('failed', 0, 'Import gagal: ' . $exception->getMessage());
// Cleanup file temporary
if (Storage::exists($this->filePath)) {
Storage::delete($this->filePath);
}
}
}

View File

@@ -6,7 +6,7 @@
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
use Mattiverse\Userstamps\Traits\Userstamps;
use Wildside\Userstamps\Userstamps;
use Illuminate\Notifications\Notifiable;

View File

@@ -1,115 +0,0 @@
<?php
namespace Modules\Lpj\Models;
use Illuminate\Foundation\Auth\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Str;
/**
* Model Bucok untuk mengelola data bucok
*
* @property int $id
* @property int|null $no
* @property string|null $tanggal
* @property string|null $bulan
* @property int|null $tahun
* @property string|null $tanggal_penuh
* @property string|null $nomor_categ
* @property string|null $coa_summary
* @property string|null $nomor_coa
* @property string|null $nama_coa
* @property string $nomor_tiket
* @property string|null $deskripsi
* @property float|null $nominal
* @property string|null $penyelesaian
* @property int|null $umur_aging
* @property string|null $cost_center
* @property string|null $nama_sub_direktorat
* @property string|null $nama_direktorat_cabang
* @property string|null $tanggal_penyelesaian
* @property float|null $nominal_penyelesaian
* @property float|null $nominal_berjalan
* @property float|null $amortisasi_berjalan
* @property float|null $sistem_berjalan
* @property float|null $lainnya_berjalan
* @property float|null $nominal_gantung
* @property float|null $aset_gantung
* @property string|null $keterangan_gantung
* @property string|null $lainnya_satu
* @property string|null $lainnya_dua
*/
class Bucok extends Base
{
use HasFactory;
/**
* Nama tabel yang digunakan oleh model
*/
protected $table = 'bucoks';
/**
* Field yang dapat diisi secara mass assignment
*/
protected $fillable = [
'no',
'tanggal',
'bulan',
'tahun',
'tanggal_penuh',
'nomor_categ',
'coa_summary',
'nomor_coa',
'nama_coa',
'nomor_tiket',
'deskripsi',
'nominal',
'penyelesaian',
'umur_aging',
'cost_center',
'nama_sub_direktorat',
'nama_direktorat_cabang',
'tanggal_penyelesaian',
'nominal_penyelesaian',
'nominal_berjalan',
'amortisasi_berjalan',
'sistem_berjalan',
'lainnya_berjalan',
'nominal_gantung',
'aset_gantung',
'keterangan_gantung',
'lainnya_satu',
'lainnya_dua',
'nomor_registrasi',
'permohonan_id',
];
/**
* Casting tipe data untuk field tertentu
*/
protected $casts = [
'no' => 'integer',
'tanggal' => 'date',
'tahun' => 'integer',
'tanggal_penuh' => 'date',
'nominal' => 'decimal:2',
'umur_aging' => 'integer',
'tanggal_penyelesaian' => 'date',
'nominal_penyelesaian' => 'decimal:2',
'nominal_berjalan' => 'decimal:2',
'amortisasi_berjalan' => 'decimal:2',
'sistem_berjalan' => 'decimal:2',
'lainnya_berjalan' => 'decimal:2',
'nominal_gantung' => 'decimal:2',
'aset_gantung' => 'decimal:2',
];
/**
* Field yang akan di-hidden saat serialization
*/
protected $hidden = [
'created_by',
'updated_by',
'deleted_by',
];
}

View File

@@ -27,7 +27,7 @@ class LampiranDokumen extends Base
{
$user = Auth::user();
if ($user && $user->hasAnyRole(['penilai', 'administrator', 'Penilai', 'admin','surveyor','pemohon-ao','pemohon-eo'])) {
if ($user && $user->hasAnyRole(['penilai', 'administrator', 'Penilai', 'admin','surveyor'])) {
$file = $fileData['file'];
$fileName = $fileData['nama_file'] ?? time() . '_' . $file->getClientOriginalName();
$filePath = $file->storeAs('lampiran_dokumen', $fileName, 'public');

View File

@@ -18,9 +18,7 @@ class LaporanAdminKredit extends Base
'tanggal_kunjungan',
'nilai_pasar_wajar',
'nilai_likuidasi',
'nama_penilai',
'keterangan',
'kolektibilitas'
'nama_penilai'
];
protected $casts = [

View File

@@ -1,114 +0,0 @@
<?php
namespace Modules\Lpj\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Modules\Usermanagement\Models\User;
class LaporanSlik extends Model
{
use HasFactory;
protected $table = 'laporan_slik';
protected $fillable = [
'slik_id',
'sandi_bank',
'kode_kantor',
'kode_cabang',
'tahun',
'bulan',
'no_rekening',
'cif',
'kode_jenis',
'kode_jenis_ket',
'kode_sifat',
'kode_sifat_ket',
'kode_valuta',
'kode_valuta_ket',
'baki_debet',
'kolektibilitas',
'kolektibilitas_ket',
'tanggal_mulai',
'tanggal_jatuh_tempo',
'tanggal_selesai',
'tanggal_restrukturisasi',
'kode_sebab_macet',
'kode_sebab_macet_ket',
'tanggal_macet',
'kode_kondisi',
'kode_kondisi_ket',
'tanggal_kondisi',
'nilai_agunan',
'nilai_agunan_ket',
'jenis_agunan',
'kode_agunan',
'kode_agunan_ket',
'peringkat_agunan',
'peringkat_agunan_ket',
'nama_debitur',
'npwp',
'no_ktp',
'no_telp',
'kode_kab_kota',
'kode_kab_kota_ket',
'kode_negara_domisili',
'kode_negara_domisili_ket',
'kode_pos',
'alamat',
'fasilitas',
'status_agunan',
'tanggal_lapor',
'status',
'created_by',
'updated_by',
];
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
public function slik()
{
return $this->belongsTo(Slik::class, 'slik_id');
}
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater()
{
return $this->belongsTo(User::class, 'updated_by');
}
/**
* Scope untuk filter berdasarkan status
*/
public function scopeActive($query)
{
return $query->where('status', 'aktif');
}
/**
* Accessor untuk nilai agunan yang diformat
*/
public function getNilaiAgunanFormattedAttribute()
{
return number_format($this->nilai_agunan ?? 0, 0, ',', '.');
}
/**
* Accessor untuk status badge
*/
public function getStatusBadgeAttribute()
{
$status = $this->status ?? 'aktif';
$class = $status == 'aktif' ? 'success' : 'danger';
return '<span class="badge badge-light-' . $class . '">' . ucfirst($status) . '</span>';
}
}

View File

@@ -6,5 +6,5 @@
class NilaiPlafond extends Base
{
protected $table = 'nilai_plafond';
protected $fillable = ['code', 'name', 'biaya'];
protected $fillable = ['code', 'name'];
}

View File

@@ -2,28 +2,41 @@
namespace Modules\Lpj\Models;
use Illuminate\Foundation\Auth\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
// use Modules\Lpj\Database\Factories\NocFactory;
class Noc extends Base
{
protected $table = 'noc';
protected $guarded = ['id'];
protected $fillable = [
'permohonan_id',
'persetujuan_penawaran_id',
'bukti_bayar',
'nominal_bayar',
'status_bayar',
'tanggal_pembayaran',
'nominal_penyelesaian',
'status_penyelesaiaan',
'tanggal_penyelesaian',
'bukti_penyelesaian',
'bukti_ksl',
'memo_penyelesaian',
'catatan_noc',
'status',
'authorized_status',
'authorized_at',
'authorized_by',
];
protected $casts = [
'nominal_bayar' => 'decimal:2',
'status_bayar' => 'boolean',
'status_kurang_bayar' => 'boolean',
'nominal_kurang_bayar' => 'decimal:2',
'status_lebih_bayar' => 'boolean',
'nominal_lebih_bayar' => 'decimal:2',
'tanggal_pembayaran' => 'date',
'nominal_penyelesaian' => 'decimal:2',
'status_penyelesaiaan' => 'boolean',
'tanggal_penyelesaian' => 'date',
'memo_penyelesaian_date' => 'date',
'memo_penyelesaian_payment_date' => 'date',
'memo_penyelesaian_created_at' => 'datetime',
'status' => 'boolean',
'authorized_status' => 'boolean',
'authorized_at' => 'datetime',
@@ -46,12 +59,4 @@ class Noc extends Base
{
return $this->belongsTo(User::class, 'authorized_by');
}
public function debiture(){
return $this->belongsTo(Debiture::class,'debiture_id');
}
public function branch(){
return $this->belongsTo(Branch::class,'branch_id');
}
}

View File

@@ -1,190 +0,0 @@
<?php
namespace Modules\Lpj\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Str;
/**
* Model Slik untuk mengelola data SLIK (Sistem Layanan Informasi Keuangan)
*
* @property int $id
* @property string|null $sandi_bank
* @property string|null $tahun
* @property string|null $bulan
* @property string|null $flag_detail
* @property string|null $kode_register_agunan
* @property string|null $no_rekening
* @property string|null $cif
* @property string|null $kolektibilitas
* @property string|null $fasilitas
* @property string|null $jenis_segmen_fasilitas
* @property string|null $status_agunan
* @property string|null $jenis_agunan
* @property string|null $peringkat_agunan
* @property string|null $lembaga_pemeringkat
* @property string|null $jenis_pengikatan
* @property string|null $tanggal_pengikatan
* @property string|null $nama_pemilik_agunan
* @property string|null $bukti_kepemilikan
* @property string|null $alamat_agunan
* @property string|null $lokasi_agunan
* @property string|null $nilai_agunan
* @property string|null $nilai_agunan_menurut_ljk
* @property string|null $tanggal_penilaian_ljk
* @property string|null $nilai_agunan_penilai_independen
* @property string|null $nama_penilai_independen
* @property string|null $tanggal_penilaian_penilai_independen
* @property string|null $jumlah_hari_tunggakan
* @property string|null $status_paripasu
* @property string|null $prosentase_paripasu
* @property string|null $status_kredit_join
* @property string|null $diasuransikan
* @property string|null $keterangan
* @property string|null $kantor_cabang
* @property string|null $operasi_data
* @property string|null $kode_cabang
* @property string|null $nama_debitur
* @property string|null $nama_cabang
* @property string|null $flag
*/
class Slik extends Base
{
use HasFactory;
/**
* Nama tabel yang digunakan oleh model
*/
protected $table = 'sliks';
/**
* Field yang dapat diisi secara mass assignment
*/
protected $fillable = [
'sandi_bank',
'tahun',
'bulan',
'flag_detail',
'kode_register_agunan',
'no_rekening',
'cif',
'kolektibilitas',
'fasilitas',
'jenis_segmen_fasilitas',
'status_agunan',
'jenis_agunan',
'peringkat_agunan',
'lembaga_pemeringkat',
'jenis_pengikatan',
'tanggal_pengikatan',
'nama_pemilik_agunan',
'bukti_kepemilikan',
'alamat_agunan',
'lokasi_agunan',
'nilai_agunan',
'nilai_agunan_menurut_ljk',
'tanggal_penilaian_ljk',
'nilai_agunan_penilai_independen',
'nama_penilai_independen',
'tanggal_penilaian_penilai_independen',
'jumlah_hari_tunggakan',
'status_paripasu',
'prosentase_paripasu',
'status_kredit_join',
'diasuransikan',
'keterangan',
'kantor_cabang',
'operasi_data',
'kode_cabang',
'nama_debitur',
'nama_cabang',
'flag',
];
/**
* Casting tipe data untuk field tertentu
*/
protected $casts = [
'tanggal_pengikatan' => 'date',
'tanggal_penilaian_ljk' => 'date',
'tanggal_penilaian_penilai_independen' => 'date',
'nilai_agunan' => 'decimal:2',
'nilai_agunan_menurut_ljk' => 'decimal:2',
'nilai_agunan_penilai_independen' => 'decimal:2',
'prosentase_paripasu' => 'decimal:2',
'jumlah_hari_tunggakan' => 'integer',
];
/**
* Accessor untuk format nilai agunan dengan currency Indonesia
*/
public function getNilaiAgunanFormattedAttribute(): string
{
return $this->nilai_agunan ? 'Rp ' . number_format($this->nilai_agunan, 0, ',', '.') : 'Rp 0';
}
/**
* Accessor untuk format nilai agunan menurut LJK dengan currency Indonesia
*/
public function getNilaiAgunanMenurutLjkFormattedAttribute(): string
{
return $this->nilai_agunan_menurut_ljk ? 'Rp ' . number_format($this->nilai_agunan_menurut_ljk, 0, ',', '.') : 'Rp 0';
}
/**
* Accessor untuk format nilai agunan penilai independen dengan currency Indonesia
*/
public function getNilaiAgunanPenilaiIndependenFormattedAttribute(): string
{
return $this->nilai_agunan_penilai_independen ? 'Rp ' . number_format($this->nilai_agunan_penilai_independen, 0, ',', '.') : 'Rp 0';
}
/**
* Accessor untuk status badge berdasarkan status agunan
*/
public function getStatusBadgeAttribute(): string
{
$statusClass = match($this->status_agunan) {
'Aktif' => 'badge-success',
'Tidak Aktif' => 'badge-danger',
'Pending' => 'badge-warning',
default => 'badge-secondary'
};
return '<span class="badge ' . $statusClass . '">' . ($this->status_agunan ?? 'Unknown') . '</span>';
}
/**
* Scope untuk filter berdasarkan tahun
*/
public function scopeByYear($query, $year)
{
return $query->where('tahun', $year);
}
/**
* Scope untuk filter berdasarkan bulan
*/
public function scopeByMonth($query, $month)
{
return $query->where('bulan', $month);
}
/**
* Scope untuk filter berdasarkan sandi bank
*/
public function scopeBySandiBank($query, $sandiBank)
{
return $query->where('sandi_bank', $sandiBank);
}
/**
* Scope untuk filter berdasarkan kode cabang
*/
public function scopeByKodeCabang($query, $kodeCabang)
{
return $query->where('kode_cabang', $kodeCabang);
}
// Method creator() dan editor() sudah disediakan oleh trait Userstamps
}

View File

@@ -1,236 +0,0 @@
<?php
namespace Modules\Lpj\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class ImportProgressService
{
protected string $cacheKeyPrefix;
protected int $cacheTtl;
/**
* Constructor
*/
public function __construct()
{
$this->cacheKeyPrefix = config('import.slik.progress.cache_key', 'slik_import_progress');
$this->cacheTtl = config('import.slik.progress.cache_ttl', 3600);
}
/**
* Start new import progress
*
* @param string $importId
* @param int $userId
* @param string $filename
* @param int $totalRows
* @return array
*/
public function start(string $importId, int $userId, string $filename, int $totalRows): array
{
$progressData = [
'import_id' => $importId,
'user_id' => $userId,
'filename' => $filename,
'total_rows' => $totalRows,
'processed_rows' => 0,
'skipped_rows' => 0,
'error_rows' => 0,
'status' => 'started',
'percentage' => 0,
'message' => 'Memulai import...',
'started_at' => now(),
'updated_at' => now()
];
$cacheKey = $this->getCacheKey($importId);
Cache::put($cacheKey, $progressData, $this->cacheTtl);
Log::info('ImportProgressService: Import started', $progressData);
return $progressData;
}
/**
* Update progress import
*
* @param string $importId
* @param int $processedRows
* @param int $skippedRows
* @param int $errorRows
* @param string|null $message
* @return array
*/
public function update(string $importId, int $processedRows, int $skippedRows, int $errorRows, ?string $message = null): array
{
$cacheKey = $this->getCacheKey($importId);
$progressData = Cache::get($cacheKey);
if (!$progressData) {
Log::warning('ImportProgressService: Progress data not found', ['import_id' => $importId]);
return [];
}
$totalRows = $progressData['total_rows'];
$percentage = $totalRows > 0 ? round(($processedRows / $totalRows) * 100, 2) : 0;
$progressData = array_merge($progressData, [
'processed_rows' => $processedRows,
'skipped_rows' => $skippedRows,
'error_rows' => $errorRows,
'percentage' => $percentage,
'message' => $message ?? "Memproses baris {$processedRows} dari {$totalRows}...",
'updated_at' => now()
]);
Cache::put($cacheKey, $progressData, $this->cacheTtl);
// Log progress setiap 10%
if ($percentage % 10 === 0) {
Log::info('ImportProgressService: Progress update', [
'import_id' => $importId,
'percentage' => $percentage,
'processed' => $processedRows,
'total' => $totalRows
]);
}
return $progressData;
}
/**
* Mark import as completed
*
* @param string $importId
* @param string|null $message
* @return array
*/
public function complete(string $importId, ?string $message = null): array
{
$cacheKey = $this->getCacheKey($importId);
$progressData = Cache::get($cacheKey);
if (!$progressData) {
Log::warning('ImportProgressService: Progress data not found for completion', ['import_id' => $importId]);
return [];
}
$progressData = array_merge($progressData, [
'status' => 'completed',
'percentage' => 100,
'message' => $message ?? 'Import berhasil diselesaikan',
'completed_at' => now(),
'updated_at' => now()
]);
Cache::put($cacheKey, $progressData, $this->cacheTtl);
Log::info('ImportProgressService: Import completed', [
'import_id' => $importId,
'total_rows' => $progressData['total_rows'],
'processed_rows' => $progressData['processed_rows'],
'skipped_rows' => $progressData['skipped_rows'],
'error_rows' => $progressData['error_rows']
]);
return $progressData;
}
/**
* Mark import as failed
*
* @param string $importId
* @param string $errorMessage
* @return array
*/
public function fail(string $importId, string $errorMessage): array
{
$cacheKey = $this->getCacheKey($importId);
$progressData = Cache::get($cacheKey);
if (!$progressData) {
Log::warning('ImportProgressService: Progress data not found for failure', ['import_id' => $importId]);
return [];
}
$progressData = array_merge($progressData, [
'status' => 'failed',
'message' => 'Import gagal: ' . $errorMessage,
'failed_at' => now(),
'updated_at' => now()
]);
Cache::put($cacheKey, $progressData, $this->cacheTtl);
Log::error('ImportProgressService: Import failed', [
'import_id' => $importId,
'error' => $errorMessage,
'progress_data' => $progressData
]);
return $progressData;
}
/**
* Get progress data
*
* @param string $importId
* @return array|null
*/
public function getProgress(string $importId): ?array
{
$cacheKey = $this->getCacheKey($importId);
return Cache::get($cacheKey);
}
/**
* Get all active imports for user
*
* @param int $userId
* @return array
*/
public function getUserImports(int $userId): array
{
$pattern = $this->cacheKeyPrefix . '_*';
$keys = Cache::get($pattern);
$imports = [];
foreach ($keys as $key) {
$data = Cache::get($key);
if ($data && $data['user_id'] === $userId) {
$imports[] = $data;
}
}
return $imports;
}
/**
* Clear progress data
*
* @param string $importId
* @return bool
*/
public function clear(string $importId): bool
{
$cacheKey = $this->getCacheKey($importId);
$result = Cache::forget($cacheKey);
Log::info('ImportProgressService: Progress data cleared', ['import_id' => $importId]);
return $result;
}
/**
* Generate cache key
*
* @param string $importId
* @return string
*/
private function getCacheKey(string $importId): string
{
return $this->cacheKeyPrefix . '_' . $importId;
}
}

View File

@@ -1,8 +1,10 @@
<?php
namespace Modules\Lpj\Services;
use Modules\Lpj\Models\Permohonan;
use Illuminate\Http\Request;
use Modules\Usermanagement\Models\User;
class LaporanUserService
{
@@ -25,36 +27,15 @@ class LaporanUserService
{
// Retrieve data from the database
$query = Permohonan::query();
$query = $query->where('status', 'done');
$query = User::query();
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = json_decode($request->get('search'));
if (isset($search->start_date) || isset($search->end_date)) {
$query->whereBetween('tanggal_permohonan', [
$search->start_date ?? '1900-01-01',
$search->end_date ?? now()->toDateString()
]);
}
// Filter by branch if provided
if (isset($search->user_id) && !empty($search->user_id)) {
$query->where('user_id', $search->user_id);
}
if (isset($search->search)) {
$query->where(function ($q) use ($search) {
$q->where('nomor_registrasi', 'LIKE', '%' . $search->search . '%');
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('jenisFasilitasKredit', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search->search . '%');
$q->orWhere('status', 'LIKE', '%' . $search->search . '%');
$q->whereRaw('LOWER(name) LIKE ?', ['%' . strtolower($search) . '%']);
$q->where('nik', 'LIKE', '%' . $search->search . '%');
});
}
}
@@ -82,28 +63,16 @@ class LaporanUserService
$filteredRecords = $query->count();
// Get the data for the current page
$data = $query->with(['debiture.branch'])->get();
$data = $query->with(['branch', 'roles'])->get();
$data = $data->map(function ($permohonan) {
$npw = 0;
if (isset($permohonan->penilai->lpj)) {
$lpj = json_decode($permohonan->penilai->lpj, true);
$npw = str_replace('.', '', $lpj['total_nilai_pasar_wajar'] ?? 0);
}
$data = $data->map(function ($user) {
return [
'id' => $permohonan->id,
'nomor_registrasi' => $permohonan->nomor_registrasi,
'branch' => $permohonan->debiture->branch?->name,
'name' => $permohonan->debiture?->name,
'pemohon' => $permohonan->creator?->name,
'tanggal_permohonan' => $permohonan->tanggal_permohonan,
'nama_penilai' => $permohonan->penilaian?->_user_penilai?->userPenilaiTeam?->name,
'tanggal_laporan' => $permohonan->approval_dd_at ?? $permohonan->approval_eo_at ?? '',
'nilai_pasar_wajar' => formatRupiah($npw, 2)
'id' => $user->id,
'nik' => $user->nik,
'name' => $user->name,
'level' => $user->roles->pluck('name')->implode(', '),
'approval_limit' => 0,
];
});

View File

@@ -221,9 +221,7 @@ class PreviewLaporanService
$statusLpj = 1;
$mig_permohonan = json_decode($permohonan->mig_permohonan);
$nilaiPasar = $mig_permohonan->mig_mst_lpj_tot_nilai_pasar ?? null;
if(($tipeLaporan->status === 'memo' && $permohonan->mig_permohonan && $permohonan->is_mig) || ($nilaiPasar !== null && $nilaiPasar < 1 && $permohonan->is_mig)){
if(($tipeLaporan->status === 'memo' && $permohonan->mig_permohonan) || $mig_permohonan->mig_mst_lpj_tot_nilai_pasar < 1){
$paths = $formFoto['upload_foto'] ?? null;
if (!is_array($paths) || empty($paths)) {

View File

@@ -450,6 +450,8 @@ class SaveFormInspesksiService
$data['perizinan'] = $perizinanData;
$partisiResult = [];
if (isset($data['partisi'])) {
foreach ($data['partisi'] as $name => $values) {

View File

@@ -2,62 +2,4 @@
return [
'name' => 'Lpj',
'import' => [
'slik' => [
// Memory limit untuk import (dalam MB)
'memory_limit' => env('SLIK_IMPORT_MEMORY_LIMIT', 1024),
// Ukuran chunk untuk processing (jumlah baris per chunk)
'chunk_size' => env('SLIK_IMPORT_CHUNK_SIZE', 50),
// Ukuran batch untuk database insert
'batch_size' => env('SLIK_IMPORT_BATCH_SIZE', 50),
// Timeout untuk import (dalam detik)
'timeout' => env('SLIK_IMPORT_TIMEOUT', 1800), // 30 menit untuk file besar
// Maksimum file size yang diizinkan (dalam MB)
'max_file_size' => env('SLIK_IMPORT_MAX_FILE_SIZE', 50),
// Enable garbage collection untuk optimasi memory
'enable_gc' => env('SLIK_IMPORT_ENABLE_GC', true),
// Enable progress logging
'enable_progress_logging' => env('SLIK_IMPORT_ENABLE_PROGRESS_LOGGING', true),
// Enable detailed error logging
'enable_error_logging' => env('SLIK_IMPORT_ENABLE_ERROR_LOGGING', true),
// XML Scanner settings untuk optimasi memory
'xml_scanner' => [
'timeout' => env('SLIK_XML_SCANNER_TIMEOUT', 1800), // 30 menit
'memory_limit' => env('SLIK_XML_SCANNER_MEMORY_LIMIT', 1024), // 1GB
'chunk_size' => env('SLIK_XML_SCANNER_CHUNK_SIZE', 50), // Lebih kecil untuk XML
],
// Queue processing untuk file besar
'queue' => [
'enabled' => env('SLIK_IMPORT_QUEUE_ENABLED', false),
'connection' => env('SLIK_IMPORT_QUEUE_CONNECTION', 'database'),
'queue_name' => env('SLIK_IMPORT_QUEUE_NAME', 'imports'),
'chunk_size' => env('SLIK_IMPORT_QUEUE_CHUNK_SIZE', 500),
],
// Progress tracking
'progress' => [
'enabled' => env('SLIK_IMPORT_PROGRESS_ENABLED', true),
'update_interval' => env('SLIK_IMPORT_PROGRESS_INTERVAL', 50), // update setiap 50 baris
'cache_key' => 'slik_import_progress',
'cache_ttl' => 3600, // 1 jam
],
],
// General import settings
'general' => [
'default_memory_limit' => env('IMPORT_DEFAULT_MEMORY_LIMIT', 128),
'max_execution_time' => env('IMPORT_MAX_EXECUTION_TIME', 300000),
'temp_directory' => env('IMPORT_TEMP_DIRECTORY', storage_path('app/temp')),
'cleanup_temp_files' => env('IMPORT_CLEANUP_TEMP_FILES', true),
],
],
];

View File

@@ -1,36 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('noc', function (Blueprint $table) {
// Field untuk informasi memo penyelesaian
$table->string('memo_penyelesaian_number')->nullable()->after('memo_penyelesaian');
$table->date('memo_penyelesaian_date')->nullable()->after('memo_penyelesaian_number');
$table->date('memo_penyelesaian_payment_date')->nullable()->after('memo_penyelesaian_date');
$table->timestamp('memo_penyelesaian_created_at')->nullable()->after('memo_penyelesaian_date');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('noc', function (Blueprint $table) {
$table->dropColumn([
'memo_penyelesaian_number',
'memo_penyelesaian_date',
'memo_penyelesaian_payment_date',
'memo_penyelesaian_created_at'
]);
});
}
};

View File

@@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('noc', function (Blueprint $table) {
$table->decimal('total_pembukuan', 10, 2)->after('nominal_bayar');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('noc', function (Blueprint $table) {
$table->dropColumn('total_pembukuan');
});
}
};

View File

@@ -1,44 +0,0 @@
<?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'
]);
});
}
};

View File

@@ -1,67 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('bucoks', function (Blueprint $table) {
$table->id();
$table->string('no')->nullable(); // Nomor urut
$table->string('tanggal')->nullable(); // Tanggal
$table->string('bulan')->nullable(); // Bulan
$table->string('tahun')->nullable(); // Tahun
$table->string('tanggal_penuh')->nullable(); // Tanggal lengkap
$table->string('nomor_categ')->nullable(); // Nomor kategori
$table->string('coa_summary')->nullable(); // COA Summary
$table->string('nomor_coa')->nullable(); // Nomor COA
$table->string('nama_coa')->nullable(); // Nama COA
$table->string('nomor_tiket')->unique(); // Nomor tiket (unique)
$table->string('deskripsi')->nullable(); // Deskripsi
$table->string('nominal')->nullable(); // Nominal
$table->string('penyelesaian')->nullable(); // Penyelesaian
$table->string('umur_aging')->nullable(); // Umur aging
$table->string('cost_center')->nullable(); // Cost center
$table->string('nama_sub_direktorat')->nullable(); // Nama sub direktorat
$table->string('nama_direktorat_cabang')->nullable(); // Nama direktorat cabang
$table->string('tanggal_penyelesaian')->nullable(); // Tanggal penyelesaian
$table->string('nominal_penyelesaian')->nullable(); // Nominal penyelesaian
$table->string('nominal_berjalan')->nullable(); // Nominal berjalan
$table->string('amortisasi_berjalan')->nullable(); // Amortisasi berjalan
$table->string('sistem_berjalan')->nullable(); // Sistem berjalan
$table->string('lainnya_berjalan')->nullable(); // Lainnya berjalan
$table->string('nominal_gantung')->nullable(); // Nominal gantung
$table->string('aset_gantung')->nullable(); // Aset gantung
$table->string('keterangan_gantung')->nullable(); // Keterangan gantung
$table->string('lainnya_satu')->nullable(); // Lainnya satu
$table->string('lainnya_dua')->nullable(); // Lainnya dua
// Standard Laravel fields
$table->timestamps();
$table->string('created_by')->nullable();
$table->string('updated_by')->nullable();
$table->string('deleted_by')->nullable();
$table->softDeletes();
// Indexes untuk performa
$table->index(['nomor_tiket']);
$table->index(['tanggal']);
$table->index(['tahun']);
$table->index(['nomor_coa']);
$table->index(['cost_center']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('bucoks');
}
};

View File

@@ -1,59 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
* Menambahkan kolom nomor_tiket ke tabel persetujuan_penawaran
*/
public function up(): void
{
Schema::table('persetujuan_penawaran', function (Blueprint $table) {
// Menambahkan kolom nomor_tiket setelah nomor_proposal_penawaran
$table->string('nomor_tiket', 100)->nullable()->after('nomor_proposal_penawaran')
->comment('Nomor tiket untuk tracking persetujuan penawaran');
$table->string('nominal_kurang_bayar')->nullable()->after('nomor_proposal_penawaran');
$table->string('bukti_ksl_kurang_bayar')->nullable()->after('nominal_kurang_bayar');
});
Schema::table('noc', function (Blueprint $table) {
// Menambahkan kolom nomor_tiket setelah nomor_proposal_penawaran
$table->string('nomor_tiket', 100)->nullable()->after('persetujuan_penawaran_id')
->comment('Nomor tiket untuk tracking persetujuan penawaran');
$table->string('nomor_rekening_lebih_bayar',20)->nullable()->after('nominal_lebih_bayar');
$table->string('bukti_ksl_lebih_bayar')->nullable()->after('nomor_rekening_lebih_bayar');
$table->string('bukti_ksl_kurang_bayar')->nullable()->after('bukti_ksl_lebih_bayar');
$table->string('nominal_pelunasan')->nullable()->after('bukti_ksl_kurang_bayar');
$table->string('debiture_id')->nullable()->after('persetujuan_penawaran_id');
});
}
/**
* Reverse the migrations.
* Menghapus kolom nomor_tiket dari tabel persetujuan_penawaran
*/
public function down(): void
{
Schema::table('persetujuan_penawaran', function (Blueprint $table) {
// Menghapus kolom nomor_tiket
$table->dropColumn('nomor_tiket');
$table->dropColumn('nominal_kurang_bayar');
$table->dropColumn('bukti_ksl_kurang_bayar');
});
Schema::table('noc', function (Blueprint $table) {
// Menghapus kolom nomor_tiket
$table->dropColumn('nomor_tiket');
$table->dropColumn('nomor_rekening_lebih_bayar');
$table->dropColumn('bukti_ksl_lebih_bayar');
$table->dropColumn('bukti_ksl_kurang_bayar');
$table->dropColumn('nominal_pelunasan');
$table->dropColumn('debiture_id');
});
}
};

View File

@@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('noc', function (Blueprint $table) {
$table->bigInteger('branch_id')->nullable()->after('debiture_id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('noc', function (Blueprint $table) {
$table->dropColumn('branch_id');
});
}
};

View File

@@ -1,88 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*
* Migration untuk membuat tabel sliks dengan semua field yang diperlukan
* berdasarkan header Excel yang diberikan untuk import data SLIK
*/
public function up(): void
{
Schema::create('sliks', function (Blueprint $table) {
$table->id();
// Field utama berdasarkan header Excel
$table->string('sandi_bank')->nullable(); // Sandi Bank
$table->string('tahun')->nullable(); // Tahun
$table->string('bulan')->nullable(); // Bulan
$table->string('flag_detail')->nullable(); // Flag Detail
$table->string('kode_register_agunan')->nullable(); // Kode Register Agunan
$table->string('no_rekening')->nullable(); // No Rekening
$table->string('cif')->nullable(); // CIF
$table->string('kolektibilitas')->nullable(); // Kolektibilitas
$table->string('fasilitas')->nullable(); // Fasilitas
$table->string('jenis_segmen_fasilitas')->nullable(); // Jenis Segmen Fasilitas
$table->string('status_agunan')->nullable(); // Status Agunan
$table->string('jenis_agunan')->nullable(); // Jenis Agunan
$table->string('peringkat_agunan')->nullable(); // Peringkat Agunan
$table->string('lembaga_pemeringkat')->nullable(); // Lembaga Pemeringkat
$table->string('jenis_pengikatan')->nullable(); // Jenis Pengikatan
$table->string('tanggal_pengikatan')->nullable(); // Tanggal Pengikatan
$table->string('nama_pemilik_agunan')->nullable(); // Nama Pemilik Agunan
$table->string('bukti_kepemilikan')->nullable(); // Bukti Kepemilikan
$table->text('alamat_agunan')->nullable(); // Alamat Agunan
$table->string('lokasi_agunan')->nullable(); // Lokasi Agunan
$table->string('nilai_agunan')->nullable(); // Nilai Agunan
$table->string('nilai_agunan_menurut_ljk')->nullable(); // Nilai Agunan Menurut LJK
$table->string('tanggal_penilaian_ljk')->nullable(); // Tanggal Penilaian LJK
$table->string('nilai_agunan_penilai_independen')->nullable(); // Nilai Agunan Penilai Independen
$table->string('nama_penilai_independen')->nullable(); // Nama Penilai Independen
$table->string('tanggal_penilaian_penilai_independen')->nullable(); // Tanggal Penilaian Penilai Independen
$table->string('jumlah_hari_tunggakan')->nullable(); // Jumlah Hari Tunggakan
$table->string('status_paripasu')->nullable(); // Status Paripasu
$table->string('prosentase_paripasu')->nullable(); // Prosentase Paripasu
$table->string('status_kredit_join')->nullable(); // Status Kredit Join
$table->string('diasuransikan')->nullable(); // Diasuransikan
$table->text('keterangan')->nullable(); // Keterangan
$table->string('kantor_cabang')->nullable(); // Kantor Cabang
$table->string('operasi_data')->nullable(); // Operasi Data
$table->string('kode_cabang')->nullable(); // Kode Cabang
$table->string('nama_debitur')->nullable(); // Nama Debitur
$table->string('nama_cabang')->nullable(); // Nama Cabang
$table->string('flag')->nullable(); // Flag
// Standard Laravel fields
$table->timestamps();
$table->string('created_by')->nullable();
$table->string('updated_by')->nullable();
$table->string('deleted_by')->nullable();
$table->softDeletes();
// Indexes untuk performa query
$table->index(['sandi_bank']);
$table->index(['tahun']);
$table->index(['bulan']);
$table->index(['no_rekening']);
$table->index(['cif']);
$table->index(['kode_register_agunan']);
$table->index(['nama_debitur']);
$table->index(['kode_cabang']);
$table->index(['created_at']);
});
}
/**
* Reverse the migrations.
*
* Menghapus tabel sliks jika migration di-rollback
*/
public function down(): void
{
Schema::dropIfExists('sliks');
}
};

View File

@@ -1,84 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('laporan_slik', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('slik_id')->nullable();
$table->string('sandi_bank', 10)->nullable();
$table->string('kode_kantor', 10)->nullable();
$table->string('kode_cabang', 10)->nullable();
$table->string('tahun', 4)->nullable();
$table->string('bulan', 2)->nullable();
$table->string('no_rekening', 50)->nullable();
$table->string('cif', 50)->nullable();
$table->string('kode_jenis', 10)->nullable();
$table->string('kode_jenis_ket', 100)->nullable();
$table->string('kode_sifat', 10)->nullable();
$table->string('kode_sifat_ket', 100)->nullable();
$table->string('kode_valuta', 5)->nullable();
$table->string('kode_valuta_ket', 50)->nullable();
$table->string('baki_debet', 20)->nullable();
$table->string('kolektibilitas', 5)->nullable();
$table->string('kolektibilitas_ket', 50)->nullable();
$table->string('tanggal_mulai', 10)->nullable();
$table->string('tanggal_jatuh_tempo', 10)->nullable();
$table->string('tanggal_selesai', 10)->nullable();
$table->string('tanggal_restrukturisasi', 10)->nullable();
$table->string('kode_sebab_macet', 10)->nullable();
$table->string('kode_sebab_macet_ket', 100)->nullable();
$table->string('tanggal_macet', 10)->nullable();
$table->string('kode_kondisi', 10)->nullable();
$table->string('kode_kondisi_ket', 100)->nullable();
$table->string('tanggal_kondisi', 10)->nullable();
$table->string('nilai_agunan', 20)->nullable();
$table->string('nilai_agunan_ket', 100)->nullable();
$table->string('jenis_agunan', 50)->nullable();
$table->string('kode_agunan', 10)->nullable();
$table->string('kode_agunan_ket', 100)->nullable();
$table->string('peringkat_agunan', 10)->nullable();
$table->string('peringkat_agunan_ket', 100)->nullable();
$table->string('nama_debitur', 100)->nullable();
$table->string('npwp', 50)->nullable();
$table->string('no_ktp', 50)->nullable();
$table->string('no_telp', 50)->nullable();
$table->string('kode_kab_kota', 10)->nullable();
$table->string('kode_kab_kota_ket', 100)->nullable();
$table->string('kode_negara_domisili', 10)->nullable();
$table->string('kode_negara_domisili_ket', 100)->nullable();
$table->string('kode_pos', 10)->nullable();
$table->string('alamat', 200)->nullable();
$table->string('fasilitas', 100)->nullable();
$table->string('status_agunan', 20)->nullable();
$table->string('tanggal_lapor', 10)->nullable();
$table->string('status', 20)->default('aktif');
$table->unsignedBigInteger('created_by')->nullable();
$table->unsignedBigInteger('updated_by')->nullable();
$table->timestamps();
$table->index('slik_id');
$table->index('no_rekening');
$table->index('cif');
$table->index('nama_debitur');
$table->index(['tahun', 'bulan']);
$table->index('created_at');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('laporan_slik');
}
};

View File

@@ -1,29 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('laporan_admin_kredit', function (Blueprint $table) {
$table->text('keterangan')->nullable()->comment('Keterangan tambahan untuk laporan admin kredit');
$table->string('kolektibilitas', 10)->nullable()->comment('Status kolektibilitas kredit');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('laporan_admin_kredit', function (Blueprint $table) {
$table->dropColumn(['keterangan', 'kolektibilitas']);
});
}
};

View File

@@ -1,52 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* Menambahkan kolom nomor_registrasi dan permohonan_id pada table bucoks
* untuk menghubungkan data bucok dengan permohonan penilaian
*/
public function up(): void
{
Schema::table('bucoks', function (Blueprint $table) {
// Menambahkan kolom nomor_registrasi untuk tracking nomor registrasi
$table->string('nomor_registrasi')->nullable()->after('nomor_tiket')->comment('Nomor registrasi terkait');
// Menambahkan kolom permohonan_id sebagai foreign key ke table permohonan
$table->unsignedBigInteger('permohonan_id')->nullable()->after('nomor_registrasi')->comment('ID permohonan terkait');
// Menambahkan foreign key constraint ke table permohonan
$table->foreign('permohonan_id')->references('id')->on('permohonan')->onDelete('set null');
// Menambahkan index untuk performa query
$table->index(['nomor_registrasi']);
$table->index(['permohonan_id']);
});
}
/**
* Reverse the migrations.
*
* Menghapus kolom nomor_registrasi dan permohonan_id dari table bucoks
*/
public function down(): void
{
Schema::table('bucoks', function (Blueprint $table) {
// Menghapus foreign key constraint terlebih dahulu
$table->dropForeign(['permohonan_id']);
// Menghapus index
$table->dropIndex(['nomor_registrasi']);
$table->dropIndex(['permohonan_id']);
// Menghapus kolom
$table->dropColumn(['nomor_registrasi', 'permohonan_id']);
});
}
};

View File

@@ -1,25 +0,0 @@
<?php
return new class extends \Illuminate\Database\Migrations\Migration
{
/**
* Tambah kolom biaya pada tabel nilai_plafond.
*/
public function up(): void
{
\Illuminate\Support\Facades\Schema::table('nilai_plafond', function (\Illuminate\Database\Schema\Blueprint $table) {
// PostgreSQL akan memetakan decimal ke numeric
$table->decimal('biaya', 15, 2)->default(0);
});
}
/**
* Menghapus kolom biaya jika dilakukan rollback.
*/
public function down(): void
{
\Illuminate\Support\Facades\Schema::table('nilai_plafond', function (\Illuminate\Database\Schema\Blueprint $table) {
$table->dropColumn('biaya');
});
}
};

View File

@@ -1,34 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Tambahkan kolom biaya pada tabel nilai_plafond
*
* Catatan:
* - Menggunakan tipe decimal(15,2) agar kompatibel dengan PostgreSQL (numeric)
*/
public function up(): void
{
Schema::table('nilai_plafond', function (Blueprint $table) {
// Tambahkan kolom biaya sebagai decimal dengan 2 pecahan
$table->decimal('biaya', 15, 2)->nullable();
});
}
/**
* Rollback perubahan: hapus kolom biaya dari tabel nilai_plafond
*/
public function down(): void
{
if (Schema::hasColumn('nilai_plafond', 'biaya')) {
Schema::table('nilai_plafond', function (Blueprint $table) {
$table->dropColumn('biaya');
});
}
}
};

View File

@@ -13,7 +13,7 @@ use Modules\Lpj\Models\Permohonan;
class MigPenilaianAndPenilainTeamSeeder extends Seeder
{
protected $errorLogFile = __DIR__ . '/csv/penilaian/penilaian.team.fix.new_20251012_error.csv';
protected $errorLogFile = __DIR__ . '/csv/penilaian/mig_penilaian_and_team_error.csv';
/**
* Run the database seeds.
@@ -23,10 +23,10 @@ class MigPenilaianAndPenilainTeamSeeder extends Seeder
// Bersihkan/Inisialisasi file error log
$this->initializeErrorLog();
// Path ke file csv
$filePath = realpath(__DIR__ . '/csv/penilaian/penilaian.team.fix.new_20251012.csv');
$filePath = realpath(__DIR__ . '/csv/penilaian/mig_penilaian_and_team.csv');
if (!$filePath) {
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/penilaian/penilaian.team.fix.new_20251012.csv');
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/penilaian/mig_penilaian_and_team.csv');
$this->command->error('File csv tidak ditemukan.');
return;
@@ -40,7 +40,7 @@ class MigPenilaianAndPenilainTeamSeeder extends Seeder
return;
}
$header = fgetcsv($handle, 0, ',');
$header = fgetcsv($handle, 0, '~');
$rows = [];
$batchSize = 500; // Ukuran batch
$userDataChace = [];
@@ -50,17 +50,17 @@ class MigPenilaianAndPenilainTeamSeeder extends Seeder
$totalData = 0;
// Menghitung total data di file CSV
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
$totalData++;
}
rewind($handle); // Reset pointer ke awal file
fgetcsv($handle, 0, ','); // Skip header
fgetcsv($handle, 0, '~'); // Skip header
$batchCount = 0;
$currentRow = 0;
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
continue;
@@ -108,19 +108,19 @@ class MigPenilaianAndPenilainTeamSeeder extends Seeder
foreach ($rows as $index => $row) {
try {
// Jalankan setiap baris dengan transaksi sendiri
//DB::beginTransaction();
DB::beginTransaction();
// Cek apakah sudah diproses
$existingRecord = Penilaian::where('nomor_registrasi', $row['mig_nomor_registrasi'])->first();
if ($existingRecord && $existingRecord->created_at) {
$this->command->info('Data sudah diproses sebelumnya: ' . $row['mig_nomor_registrasi']);
//continue;
continue;
}
// Ambil nomor registrasi
$nomor_registrasi = $this->getNomorRegistrasiPermohonan($row['mig_nomor_registrasi'], $nomorRegisCahce);
if (!$nomor_registrasi) {
throw new \Exception($row['mig_nomor_registrasi']."Nomor registrasi tidak valid");
throw new \Exception("Nomor registrasi tidak valid");
}
// Ambil user ID
@@ -136,7 +136,7 @@ class MigPenilaianAndPenilainTeamSeeder extends Seeder
if (!$userIdUpdate || !$userIdOtorisasi) {
// $this->logError($userIdUpdate, 'Salah satu user tidak ditemukan');
//continue;
continue;
}
// Mapping field user
@@ -152,32 +152,8 @@ class MigPenilaianAndPenilainTeamSeeder extends Seeder
throw new \Exception("Team tidak ditemukan");
}
$idPenilaian = Penilaian::orderBy('id', 'desc')->first()->id;
$data = [
'id' => $idPenilaian+1,
'nomor_registrasi' => $nomor_registrasi,
'jenis_penilaian_id' => 1,
'tanggal_kunjungan' => $this->parseTimestamp($row['mig_tanggal_kunjungan']) ?? now(),
'waktu_penilaian' => $this->parseTimestamp($row['mig_waktu_penilaian']),
'status' => 'done',
'keterangan' => null,
'created_at' => $this->parseTimestamp($row['mig_created_at']),
'updated_at' => $this->parseTimestamp($row['mig_updated_at']),
'authorized_by' => $mapUser['authorized_by'],
'created_by' => $mapUser['created_by'],
'updated_by' => $mapUser['updated_by']
];
if($nomor_registrasi=='251722'){
Log::info("Data penilaian 251722 : ",$data);
}
// Buat penilaian
$penilaian = Penilaian::updateOrCreate([
'nomor_registrasi' => $nomor_registrasi
],[
'id' => $idPenilaian+1,
$penilaian = Penilaian::create([
'nomor_registrasi' => $nomor_registrasi,
'jenis_penilaian_id' => 1,
'tanggal_kunjungan' => $this->parseTimestamp($row['mig_tanggal_kunjungan']) ?? now(),
@@ -191,17 +167,10 @@ class MigPenilaianAndPenilainTeamSeeder extends Seeder
'updated_by' => $mapUser['updated_by'],
]);
//DB::commit();
// Buat tim penilaian
$roles = ['surveyor', 'penilai'];
foreach ($roles as $role) {
$idPenilaianTeam = PenilaianTeam::orderBy('id', 'desc')->first()->id;
PenilaianTeam::updateOrCreate([
'penilaian_id' => $penilaian->id
],[
'id' => $idPenilaianTeam+1,
PenilaianTeam::create([
'penilaian_id' => $penilaian->id,
'user_id' => $userId,
'role' => $role,
@@ -212,12 +181,12 @@ class MigPenilaianAndPenilainTeamSeeder extends Seeder
}
// Commit transaksi
//DB::commit();
DB::commit();
$this->command->info('Proses data penilaian ' . $row['mig_nomor_lpj'] . ' (' . ($index + 1) . '/' . count($rows) . ')');
} catch (\Exception $e) {
// Rollback jika ada error
//DB::rollBack();
DB::rollBack();
Log::error('Error pada baris: ' . json_encode($row) . '. Pesan: ' . $e->getMessage());
$errorCount++;
@@ -307,13 +276,7 @@ class MigPenilaianAndPenilainTeamSeeder extends Seeder
->toDateTimeString();
}
// Format lengkap (Y-m-d H:i:s)
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
// Format d/m/Y H:i:s (contoh: 28/4/2017 14:43:43)
if (preg_match('/^\d{1,2}\/\d{1,2}\/\d{4} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $timestamp)->toDateTimeString();
}
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
return null;
} catch (\Exception $e) {

View File

@@ -23,10 +23,10 @@ class MigrationDebitureSeeder extends Seeder
$this->initializeErrorLog();
// Path ke file csv
$filePath = realpath(__DIR__ . '/csv/debitures/debitur.latest.csv');
$filePath = realpath(__DIR__ . '/csv/debitures/mig_debiture.csv');
if (!$filePath) {
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/debitures/debitur.latest.csv');
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/debitures/mig_debiture.csv');
$this->command->error('File csv tidak ditemukan.');
return;
}
@@ -37,7 +37,7 @@ class MigrationDebitureSeeder extends Seeder
return;
}
$header = fgetcsv($handle, 0, '|');
$header = fgetcsv($handle, 0, '~');
$rows = [];
$batchSize = 500;
$userData = [];
@@ -45,17 +45,17 @@ class MigrationDebitureSeeder extends Seeder
$totalData = 0;
// Hitung total baris
while (($data = fgetcsv($handle, 0, '|')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
$totalData++;
}
rewind($handle);
fgetcsv($handle, 0, '|'); // Lewati header
fgetcsv($handle, 0, '~'); // Lewati header
$currentRow = 0;
$batchCount = 0;
while (($data = fgetcsv($handle, 0, '|')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
$this->logError($data[0] ?? '-', 'Jumlah kolom tidak sesuai');
continue;
@@ -94,14 +94,14 @@ class MigrationDebitureSeeder extends Seeder
$existingRecord = Debiture::where('mig_kd_debitur_seq', $kode)->first();
if ($existingRecord && $existingRecord->processed_at) {
$this->command->info("Data sudah diproses sebelumnya: $kode");
//continue;
continue;
}
// Ambil branch_id
$branchId = $this->getBranchId($row['mig_kd_cabang'] ?? null, $branchCache);
if (!$branchId) {
$this->logError($row['mig_kd_cabang'], 'Cabang tidak ditemukan');
//continue;
$this->logError($kode, 'Cabang tidak ditemukan');
continue;
}
// Ambil user IDs berdasarkan NIK
@@ -136,12 +136,10 @@ class MigrationDebitureSeeder extends Seeder
}
// Simpan data
Debiture::updateOrCreate([
'mig_kd_debitur_seq' => (int) strtok($row['mig_kd_debitur_seq'], '.'),
], [
Debiture::create([
'branch_id' => $branchId,
'name' => $row['name'] ?? '',
'cif' => (int) strtok($row['cif'],'.') ?: '0000000000',
'cif' => $row['cif'] ?: '0000000000',
'phone' => $nomorHp,
'nomor_id' => $nomorId,
'email' => $email,
@@ -153,9 +151,8 @@ class MigrationDebitureSeeder extends Seeder
'updated_by' => $mapUser['updated_by'] ?? null,
'created_at' => $createdAt,
'updated_at' => $updatedAt,
'mig_kd_debitur_seq' => (int) strtok($row['mig_kd_debitur_seq'], '.'),
'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
'processed_at' => now(),
'mig_debitur' => json_encode($row),
'is_mig' => 1
]);
@@ -214,13 +211,7 @@ class MigrationDebitureSeeder extends Seeder
->toDateTimeString();
}
// Format lengkap (Y-m-d H:i:s)
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
// Format d/m/Y H:i:s (contoh: 28/4/2017 14:43:43)
if (preg_match('/^\d{1,2}\/\d{1,2}\/\d{4} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $timestamp)->toDateTimeString();
}
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
return null;
} catch (\Exception $e) {

View File

@@ -19,7 +19,7 @@ use Illuminate\Support\Facades\Log;
class MigrationDetailDokumenJaminanSeeder extends Seeder
{
protected $errorLogFile = __DIR__ . '/csv/detail.dokumen.jaminan_20251016_error.csv';
protected $errorLogFile = __DIR__ . '/csv/detail-dokumen/mig_detail_dokument_external_error.csv';
/**
* Run the database seeds.
*/
@@ -28,10 +28,10 @@ class MigrationDetailDokumenJaminanSeeder extends Seeder
{
$this->initializeErrorLog();
// Path ke file csv
$filePath = realpath(__DIR__ . '/csv/detail.dokumen.jaminan_20251016.csv');
$filePath = realpath(__DIR__ . '/csv/detail-dokumen/mig_detail_dokument_external.csv');
if (!$filePath) {
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/detail.dokumen.jaminan_20251016.csv');
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/detail-dokumen/mig_detail_dokument_external.csv');
$this->command->error('File csv tidak ditemukan.');
return;
}
@@ -42,7 +42,7 @@ class MigrationDetailDokumenJaminanSeeder extends Seeder
return;
}
$header = fgetcsv($handle, 0, ',');
$header = fgetcsv($handle, 0, '~');
$rows = [];
$batchSize = 1000; // Ukuran batch
$permohonanCache = [];
@@ -58,19 +58,19 @@ class MigrationDetailDokumenJaminanSeeder extends Seeder
$hubunganPemilikCache = [];
// Menghitung total data di file CSV
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
$totalData++;
}
rewind($handle); // Reset pointer ke awal file
fgetcsv($handle, 0, ','); // Skip header
fgetcsv($handle, 0, '~'); // Skip header
$batchCount = 0;
$currentRow = 0;
// Membaca setiap baris dalam CSV
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
$errorCount++;
@@ -123,14 +123,11 @@ class MigrationDetailDokumenJaminanSeeder extends Seeder
// Ambil salah satu baris untuk referensi (misal baris pertama dari grup)
$firstRow = reset($dataPerGrup)['details'] ? reset($dataPerGrup) : reset($dataPerGrup);
$debiturId = null;
$nomorJaminan = null;
// Cari debitur ID dari salah satu field
foreach ($rows as $row) {
if ($row['mig_nomor_laporan'] == $nomorLaporan) {
$debiturId = $row['mig_kd_debitur_seq'] ?? null;
$nomorJaminan = $row['mig_nomor_jaminan'] ?? null;
break;
}
}
@@ -139,27 +136,21 @@ class MigrationDetailDokumenJaminanSeeder extends Seeder
throw new \Exception('Debitur ID tidak ditemukan');
}
// Ambil ID dokumen jaminan
$dokumenJaminanId = $this->getDokumenJaminanId($debiturId,$nomorJaminan, $dokumenJaminanCache);
$dokumenJaminanId = $this->getDokumenJaminanId($debiturId, $dokumenJaminanCache);
if (!$dokumenJaminanId) {
throw new \Exception('Dokumen jaminan tidak ditemukan');
}
//dd($debiturId,$nomorJaminan);
foreach ($dataPerGrup as $groupKey => $rowData) {
// Ambil legalitas jaminan ID berdasarkan grup keterangan
$legalitasJaminanId = $this->getLegalitasJaminanId($groupKey, $jenisJaminanCache);
if (!$legalitasJaminanId) {
throw new \Exception("Legalitas jaminan tidak ditemukan untuk grup: {$groupKey}");
}
// Simpan ke database
$detail =DetailDokumenJaminan::updateOrCreate(
DetailDokumenJaminan::updateOrCreate(
[
'name' => $groupKey,
'dokumen_jaminan_id' => $dokumenJaminanId,
@@ -172,10 +163,6 @@ class MigrationDetailDokumenJaminanSeeder extends Seeder
'keterangan' => $groupKey,
]
);
if($nomorJaminan=='253339'){
//dd($debiturId,$nomorJaminan,$legalitasJaminanId, $detail, $dokumenJaminanId);
}
}
// Info progress
@@ -302,11 +289,13 @@ class MigrationDetailDokumenJaminanSeeder extends Seeder
return $finalGrouped;
}
private function getDokumenJaminanId(string $code,string $nomorLaporan, array &$dokumenJaminanCache)
private function getDokumenJaminanId(string $code, array &$dokumenJaminanCache)
{
$dokumen = DokumenJaminan::where('mig_kd_debitur_seq', $code)
->where('nomor_lpj', $nomorLaporan)
->first();
if (isset($dokumenJaminanCache[$code])) {
return $dokumenJaminanCache[$code];
}
$dokumen = DokumenJaminan::where('mig_kd_debitur_seq', $code)->first();
if ($dokumen) {
$dokumenJaminanCache[$code] = $dokumen->id;

View File

@@ -19,7 +19,7 @@ use Illuminate\Support\Facades\Log;
class MigrationDokumentJaminanSeeder extends Seeder
{
protected $errorLogFile = __DIR__ . '/csv/dokumen_20251022_error_log.csv';
protected $errorLogFile = __DIR__ . '/csv/dokumen-pemilik/mig_pemilik_dan_dokument_external_error_log.csv';
/**
* Run the database seeds.
*/
@@ -27,10 +27,10 @@ class MigrationDokumentJaminanSeeder extends Seeder
{
$this->initializeErrorLog();
// Path ke file csv
$filePath = realpath(__DIR__ . '/csv/dokumen_20251022.csv');
$filePath = realpath(__DIR__ . '/csv/dokumen-pemilik/mig_pemilik_dan_dokument_external.csv');
if (!$filePath) {
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/dokumen_20251022.csv');
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/dokumen-pemilik/mig_pemilik_dan_dokument_external.csv');
$this->command->error('File csv tidak ditemukan.');
return;
}
@@ -41,7 +41,7 @@ class MigrationDokumentJaminanSeeder extends Seeder
return;
}
$header = fgetcsv($handle, 0, ',');
$header = fgetcsv($handle, 0, '~');
$rows = [];
$batchSize = 1000; // Ukuran batch
$permohonanCache = [];
@@ -56,19 +56,19 @@ class MigrationDokumentJaminanSeeder extends Seeder
$errorDebitureIds = [];
$hubunganPemilikCache = [];
// Menghitung total data di file CSV
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
$totalData++;
}
rewind($handle); // Reset pointer ke awal file
fgetcsv($handle, 0, ','); // Skip header
fgetcsv($handle, 0, '~'); // Skip header
$batchCount = 0;
$currentRow = 0;
// Membaca setiap baris dalam CSV
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
$errorCount++;
@@ -122,7 +122,7 @@ class MigrationDokumentJaminanSeeder extends Seeder
foreach ($rows as $index => $row) {
try {
// Jalankan transaksi per-baris
// DB::beginTransaction();
DB::beginTransaction();
// Cari permohonan
@@ -136,8 +136,8 @@ class MigrationDokumentJaminanSeeder extends Seeder
// Pastikan permohonan_id belum digunakan di dokumen_jaminan
$existingDokumen = DokumenJaminan::where('permohonan_id', $permohonan->id)->first();
if ($existingDokumen) {
//throw new \Exception("permohonan_id {$permohonan->id} sudah digunakan di dokumen_jaminan");
//continue;
throw new \Exception("permohonan_id {$permohonan->id} sudah digunakan di dokumen_jaminan");
continue;
}
// Ambil lokasi
@@ -177,42 +177,37 @@ class MigrationDokumentJaminanSeeder extends Seeder
'is_mig' => 1
]);
// Buat Dokumen Jaminan
$dokumenJaminan = DokumenJaminan::updateOrCreate([
'permohonan_id' => $permohonan->id,
'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
'nomor_lpj' => $row['mig_nomor_jaminan']
],[
DokumenJaminan::updateOrCreate([
'debiture_id' => $permohonan->debiture_id,
'permohonan_id' => $permohonan->id,
'jenis_jaminan_id' => $this->getJaminanId($row['mig_jenis_seq']) ?? '',
'jenis_jaminan_id' => $this->getJaminanId($row['mig_jenis_seq']),
'pemilik_jaminan_id' => $pemilik_jaminan->id,
// jika external silahkan matikan ini
'province_code' => $proviceCode ?? '',
'city_code' => $cityCode ?? '',
'district_code' => $districtCode ?? '',
'village_code' => $subdistrict['code'] ?? '',
'postal_code' => $subdistrict['postal_code'] ?? '',
// 'province_code' => $proviceCode,
// 'city_code' => $cityCode,
// 'district_code' => $districtCode,
// 'village_code' => $subdistrict['code'],
// 'postal_code' => $subdistrict['postal_code'],
'address' => $row['address'],
'created_at' => $this->parseTimestamp($row['created_at']),
'updated_at' => $this->parseTimestamp($row['updated_at']),
'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
'processed_at' => now(),
'nomor_lpj' => $row['mig_nomor_jaminan'],
'is_mig' => 1
]);
// Commit jika semua sukses
// DB::commit();
DB::commit();
$this->command->info("Proses dokumen jaminan: " . $row['mig_kd_debitur_seq'] . "Batch: {$batchCount} Baris: {$currentRow} Total: {$totalData} Error: {$errorCount}");
} catch (\Exception $e) {
// Rollback hanya untuk baris ini
DB::rollBack();
Log::error("Error pada baris: " . json_encode($row) . ". Pesan: " . $e->getMessage());
$this->logError($row['mig_kd_debitur_seq'] ?? '-', $e->getMessage());
$errorDebitureIds[] = $row['mig_kd_debitur_seq'] ?? '-';
// DB::rollBack();
continue;
}
}
@@ -238,7 +233,7 @@ class MigrationDokumentJaminanSeeder extends Seeder
private function getJaminanId($code): ?int
{
/*$mapping = [
$mapping = [
7 => 17,
8 => 13,
6 => 32,
@@ -261,62 +256,6 @@ class MigrationDokumentJaminanSeeder extends Seeder
123382184742 => 18,
138051483711 => 44,
991 => 52
];*/
$mapping = [
1 => 1,
2 => 17,
3 => 19,
4 => 15,
5 => 18,
6 => 26,
7 => 26,
8 => 26,
9 => 26,
10 => 24,
11 => 28,
12 => 29,
13 => 32,
991001 => 17,
121965631354 => 17,
122267387302 => 13,
122267391891 => 27,
123242566528 => 1,
123391628912 => 18,
123779076991 => 26,
123779092232 => 26,
123837866231 => 19,
124159228236 => 14,
124280447242 => 32,
124385048902 => 30,
124539856281 => 22,
124635294016 => 13,
124963468687 => 18,
125178371127 => 31,
125228814911 => 17,
125749523699 => 27,
126156105725 => 15,
127407367039 => 15,
132065123922 => 32,
138027244724 => 33,
138027246193 => 34,
138027693348 => 35,
138027764236 => 10,
138050882693 => 15,
138050910670 => 20,
138051316169 => 23,
138051517359 => 36,
138051519318 => 37,
138051522331 => 38,
138051601738 => 39,
138051602831 => 40,
138051773783 => 41,
138051776693 => 46,
138051780489 => 42,
164921358499 => 32,
165216289979 => 49,
165216294371 => 49,
173035895092 => 24
];
return $mapping[$code] ?? 17;
@@ -469,13 +408,7 @@ class MigrationDokumentJaminanSeeder extends Seeder
->toDateTimeString();
}
// Format lengkap (Y-m-d H:i:s)
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
// Format d/m/Y H:i:s (contoh: 28/4/2017 14:43:43)
if (preg_match('/^\d{1,2}\/\d{1,2}\/\d{4} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $timestamp)->toDateTimeString();
}
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
return null;
} catch (\Exception $e) {

View File

@@ -16,7 +16,7 @@ class MigrationGambarInspeksiSeeder extends Seeder
/**
* Run the database seeds.
*/
protected $errorLogFile = __DIR__ . '/csv/inspeksi/foto_20251014-error.csv';
protected $errorLogFile = __DIR__ . '/csv/inspeksi/mig_inspeksi_foto_20253_error.csv';
/**
* Migrasi data gambar inspeksi dari file csv ke tabel inspeksi_gambar
*
@@ -26,10 +26,10 @@ class MigrationGambarInspeksiSeeder extends Seeder
{
$this->initializeErrorLog();
// Path ke file csv
$filePath = realpath(__DIR__ . '/csv/inspeksi/foto_20251014.csv');
$filePath = realpath(__DIR__ . '/csv/inspeksi/mig_inspeksi_foto_20253.csv');
if (!$filePath) {
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/inspeksi/foto_20251014.csv');
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/inspeksi/mig_inspeksi_foto_20253.csv');
$this->command->error('File csv tidak ditemukan.');
return;
}
@@ -40,26 +40,26 @@ class MigrationGambarInspeksiSeeder extends Seeder
return;
}
$header = fgetcsv($handle, 0, ',');
$header = fgetcsv($handle, 0, '~');
$rows = [];
$batchSize = 500;
$userData = [];
$branchCache = []; // <-- Gunakan sebagai cache
$totalData = 0;
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
$totalData++;
}
rewind($handle);
fgetcsv($handle, 0, ','); // Skip header
fgetcsv($handle, 0, '~'); // Skip header
$batchCount = 0;
$currentRow = 0;
$errorCount = 0;
$errorDebitureIds = [];
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
continue;
@@ -106,17 +106,13 @@ class MigrationGambarInspeksiSeeder extends Seeder
// Bangun JSON foto_form
$fotoJson = $this->checkFoto($groupRows);
Inspeksi::updateOrCreate(
[
'permohonan_id' => $nomorRegis['id'],
'dokument_id' => $nomorRegis['dokument_id']
],
[
'foto_form' => $fotoJson,
'updated_at' => now()
]
);
Inspeksi::where('permohonan_id', $nomorRegis['id'])
->where('dokument_id', $nomorRegis['dokument_id'])
->whereNotNull('data_form')
->update([
'foto_form' => $fotoJson,
'updated_at' => now()
]);
$this->command->info("Berhasil update foto_form untuk nomor jaminan: {$nomorJaminan}");
@@ -154,7 +150,6 @@ class MigrationGambarInspeksiSeeder extends Seeder
'PETA LOKASI' => [],
'GAMBAR SITUASI / SURAT UKUR' => [],
'FOTO JAMINAN' => [],
'MAK' => [],
'lainnya' => []
];
@@ -172,7 +167,7 @@ class MigrationGambarInspeksiSeeder extends Seeder
}
try {
$tanggal = \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $tgl)->startOfDay();
$tanggal = \Carbon\Carbon::createFromFormat('Y-m-d', $tgl);
if (!$tanggal) {
throw new \Exception("Tanggal tidak valid");
}

View File

@@ -18,14 +18,7 @@ use Modules\Location\Models\Village;
require_once __DIR__ . '/MigInspeksiHelper.php';
class MigrationInpseksiSeeder extends Seeder
{
protected $errorLogFile = __DIR__ . '/csv/inspeksi/inspeksi.09_error.csv';
/**
* Properti command disediakan oleh Laravel saat seeder dijalankan melalui Artisan.
* Dideklarasikan untuk menghindari error linter pada akses properti.
*
* @var mixed|null
*/
protected $command;
protected $errorLogFile = __DIR__ . '/csv/inspeksi/mig_inspeksi_2025_error.csv';
// protected $fielKesimpulan = ;
/**
* Run the database seeds.
@@ -34,46 +27,44 @@ class MigrationInpseksiSeeder extends Seeder
{
$this->initializeErrorLog();
// Path ke file csv
$filePath = realpath(__DIR__ . '/csv/inspeksi/inspeksi.09.csv');
$filePath = realpath(__DIR__ . '/csv/inspeksi/mig_inspeksi_2025.csv');
if (!$filePath) {
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/inspeksi/inspeksi.09.csv');
\Illuminate\Support\Facades\Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/inspeksi/inspeksi.09.csv');
$this->command->error('File csv tidak ditemukan.');
return;
}
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/inspeksi/mig_inspeksi_2025.csv');
$this->command->error('File csv tidak ditemukan.');
return;
}
if (($handle = fopen($filePath, 'r')) === false) {
if (($handle = fopen($filePath, 'r')) === false) {
Log::error('Gagal membuka file CSV: ' . $filePath);
\Illuminate\Support\Facades\Log::error('Gagal membuka file CSV: ' . $filePath);
$this->command->error('Gagal membuka file CSV.');
return;
}
$this->command->error('Gagal membuka file CSV.');
return;
}
$header = fgetcsv($handle, 0, ',');
$header = fgetcsv($handle, 0, '~');
$rows = [];
$batchSize = 500;
$userData = [];
$branchCache = []; // <-- Gunakan sebagai cache
$totalData = 0;
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
$totalData++;
}
rewind($handle);
fgetcsv($handle, 0, ','); // Skip header
fgetcsv($handle, 0, '~'); // Skip header
$batchCount = 0;
$currentRow = 0;
$errorCount = 0;
$errorDebitureIds = [];
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
continue;
}
}
$rows[] = array_combine($header, $data);
$currentRow++;
@@ -128,8 +119,8 @@ class MigrationInpseksiSeeder extends Seeder
->first();
if ($existingRecord) {
//$this->command->info("Data untuk nomor jaminan {$nomorJaminan} sudah ada. Lewati...");
//continue;
$this->command->info("Data untuk nomor jaminan {$nomorJaminan} sudah ada. Lewati...");
continue;
}
// Ambil baris pertama untuk created_at/updated_at
@@ -146,18 +137,8 @@ class MigrationInpseksiSeeder extends Seeder
continue;
}
// Bangun JSON terkelompok berdasarkan mig_jenis_legalitas_jaminan_name
$groupedLegalitasJson = $this->buildGroupedLegalitas($groupRows);
Log::info("Grouped legalitas JSON dibangun untuk nomor jaminan {$nomorJaminan}.", ['length' => strlen($groupedLegalitasJson ?? '')]);
// Gunakan transaksi saat menyimpan
DB::beginTransaction();
Inspeksi::updateOrCreate([
'permohonan_id' => $nomorRegis['id'] ?? null,
'dokument_id' => $nomorRegis['dokument_id'] ?? null,
],[
// Simpan ke database hanya sekali per grup
Inspeksi::create([
'name' => $jenisJaminan,
'data_form' => $dataFormJson,
'foto_form' => null,
@@ -168,16 +149,12 @@ class MigrationInpseksiSeeder extends Seeder
'updated_at' => $this->parseTimestamp($firstRow['updated_at'] ?? null),
'nomor_lpj' => $nomorJaminan,
'processed_at' => now(),
'mig_detail_data_jaminan' => $this->kv_to_json($groupRows[0]['alamat_bundle'] ?? null),
'mig_detail_legalitas_jaminan' => $groupedLegalitasJson,
'is_mig' => 1
]);
DB::commit();
$this->command->info("Berhasil simpan data inspeksi untuk nomor jaminan: {$nomorJaminan}");
} catch (\Exception $e) {
DB::rollBack();
Log::error("Error pada nomor jaminan {$nomorJaminan}: " . $e->getMessage());
$errorCount++;
$errorDebitureIds[] = $nomorJaminan;
@@ -256,7 +233,7 @@ private function buildFullDataForm(array $rows)
private function loadKesimpulanByNomorLpj($nomorLpj)
{
$filePath = realpath(__DIR__ . '/csv/inspeksi/kesimpulan.09.csv');
$filePath = realpath(__DIR__ . '/csv/inspeksi/mig_inspeksi_kesimpulan_2025.csv');
if (!$filePath || !$nomorLpj) {
return [];
@@ -267,10 +244,10 @@ private function buildFullDataForm(array $rows)
return [];
}
$header = fgetcsv($handle, 0, ',');
$header = fgetcsv($handle, 0, '~');
$result = [];
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
continue;
}
@@ -282,8 +259,9 @@ private function buildFullDataForm(array $rows)
}
}
fclose($handle);
// print_r($result);
fclose($handle);
return $result;
}
@@ -428,10 +406,10 @@ private function buildFullDataForm(array $rows)
$districtCache = [];
$subdistrictCache = [];
$proviceCode = $this->getProvinceCode($firstRow['mig_province_name'] ?? '', $provinceCache);
$proviceCode = $this->getProvinceCode($firstRow['mig_village_name'] ?? '', $provinceCache);
$cityCode = $this->getCityCode($firstRow['mig_city_name'] ?? '', $cityCache);
$districtCode = $this->getDistrictCode($firstRow['mig_district_name'] ?? '', $districtCache);
$subdistrict = $this->getSubdistrictCode($firstRow['mig_village_name'] ?? '', $subdistrictCache, $districtCache);
$subdistrict = $this->getSubdistrictCode($firstRow['mig_province_name'] ?? '', $subdistrictCache, $districtCache);
// Proses hubungan pemilik/penghuni & jenis asset dari seluruh rows
foreach ($rows as $row) {
@@ -546,7 +524,7 @@ private function buildFullDataForm(array $rows)
];
foreach ($rows as $row) {
$jenis = trim($row['mig_keterangan']); // misal: Fakta Positif, Fakta Negatif, dll
$jenis = trim($row['mig_keterangan']); // misal: Fakta Positif, Fakta Negatif, dll
$keterangan = trim($row['mig_kesimpulan']);
switch ($jenis) {
@@ -575,10 +553,10 @@ private function buildFullDataForm(array $rows)
break;
case 'CATATAN YANG PERLU DIPERHATIKAN':
$fakta['keterangan']['CATATAN YANG PERLU DIPERHATIKAN'][] = $keterangan;
$fakta['keterangan'][] = $keterangan;
break;
case 'Catatan':
$fakta['keterangan']['Catatan'][] = $keterangan;
$fakta['keterangan'][] = $keterangan;
break;
case 'Saran':
$fakta['saran'][] = $keterangan;
@@ -605,13 +583,7 @@ private function buildFullDataForm(array $rows)
->toDateTimeString();
}
// Format lengkap (Y-m-d H:i:s)
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
// Format d/m/Y H:i:s (contoh: 28/4/2047 14:43:43)
if (preg_match('/^\d{1,2}\/\d{1,2}\/\d{4} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $timestamp)->toDateTimeString();
}
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
return null;
} catch (\Exception $e) {
@@ -822,17 +794,12 @@ private function buildFullDataForm(array $rows)
$nomorLpj = $rows[0]['mig_nomor_lpj'] ?? null;
$kesimpulanRows = $this->loadKesimpulanByNomorLpj($nomorLpj);
$faktaJson = [];
if (!empty($kesimpulanRows)) {
$faktaResult = $this->checkKesimpulan($kesimpulanRows);
$faktaJson = json_decode($faktaResult[0] ?? '', true)['fakta'] ?? [];
}
if($nomorLpj === '252845'){
//dd($kesimpulanRows,$faktaResult, $faktaJson);
}
return json_encode([
'asset' => $assetJson['asset'] ?? [],
'tanah' => $tanahJson['tanah'] ?? [],
@@ -886,116 +853,6 @@ private function buildFullDataForm(array $rows)
'message' => 'Jenis aset tidak dikenali: ' . $input,
'data' => $rows
]);
}
}
/**
* Membangun JSON terstruktur yang dikelompokkan berdasarkan judul "mig_jenis_legalitas_jaminan_name".
* Setiap item berisi pasangan label dan value sesuai kolom "name" dan nilai dari
* "mig_name_keterangan_lain" (prioritas) atau "mig_name_keterangan" jika yang lain kosong.
*
* Contoh hasil:
* {
* "STATUS KEPEMILIKAN, HUBUNGAN DAN PENGHUNI": [
* {"label": "Hubungan Penghuni Jaminan dengan Debitur", "value": "Kosong"},
* {"label": "Hubungan Pemilik Jaminan dengan Debitur", "value": "SHGB An Developer"}
* ],
* "ANALISA TANAH DAN BANGUNAN": [
* {"label": "Partisi", "value": "Batako"}
* ]
* }
*
* Logging: Mencatat awal proses, jumlah kategori yang dibangun, dan panjang JSON hasil.
*
* @param array $rows Baris-baris CSV untuk satu nomor jaminan
* @return string|null JSON terstruktur atau null bila tidak ada data
*/
private function buildGroupedLegalitas(array $rows): ?string
{
Log::info('buildGroupedLegalitas: mulai membangun struktur JSON dari baris CSV', [
'rows_count' => count($rows)
]);
$grouped = [];
foreach ($rows as $row) {
$title = trim($row['mig_jenis_legalitas_jaminan_name'] ?? '');
if ($title === '') {
// Lewati baris tanpa judul kategori
continue;
}
$label = trim($row['name'] ?? '');
//$valueRaw = $row['mig_name_keterangan_lain'] ?? $row['mig_name_keterangan'] ?? null;
$other = trim((string)($row['mig_name_keterangan_lain'] ?? ''));
$val = trim((string)($row['mig_name_keterangan'] ?? ''));
$value = $other !== '' ? $val.', '.$other : $val;
if (!array_key_exists($title, $grouped)) {
$grouped[$title] = [];
}
// Simpan pasangan label-value sesuai spesifikasi
$grouped[$title][] = [
'label' => $label,
'value' => $value
];
}
if (empty($grouped)) {
Log::warning('buildGroupedLegalitas: tidak ada data terkelompok, mengembalikan null');
return null;
}
$json = json_encode($grouped, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
Log::info('buildGroupedLegalitas: selesai membangun JSON', [
'categories' => count($grouped),
'length' => strlen($json)
]);
return $json;
}
function kv_to_json(string $input, string $pairSep = ';', string $kvSep = ':'): string {
if($input === ''){
return '';
}
// Pecah berdasarkan ';' dan bersihkan spasi
$pairs = preg_split('/\s*' . preg_quote($pairSep, '/') . '\s*/u', trim($input), -1, PREG_SPLIT_NO_EMPTY);
$result = [];
$counts = [];
foreach ($pairs as $pair) {
// Bagi hanya pada pemisah ':' pertama agar nilai yang masih mengandung ':' tetap utuh
$parts = preg_split('/\s*' . preg_quote($kvSep, '/') . '\s*/u', $pair, 2);
if (count($parts) < 2) {
// jika format tidak valid, skip
continue;
}
[$rawKey, $rawVal] = $parts;
// Normalisasi ringan: trim & rapikan spasi berlebih pada key
$key = trim(preg_replace('/\s+/u', ' ', $rawKey));
$val = trim($rawVal);
// Tangani duplikasi key
if (isset($counts[$key])) {
$counts[$key]++;
$useKey = $key . '_' . $counts[$key]; // Atas Nama_2, Atas Nama_3, ...
} else {
$counts[$key] = 1;
$useKey = $key; // pertama kali pakai nama aslinya
}
$result[$useKey] = $val;
}
return json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
}
}
}

View File

@@ -12,17 +12,17 @@ use Modules\Lpj\Models\Permohonan;
class MigrationPembandingSeeder extends Seeder
{
protected $errorLogFile = __DIR__ . '/csv/inspeksi/pembanding_20251016_error.csv';
protected $errorLogFile = __DIR__ . '/csv/inspeksi/mig_pembanding_2025_error.csv';
/**
* Run the database seeds.
*/
public function run()
{
// Path ke file csv
$filePath = realpath(__DIR__ . '/csv/inspeksi/pembanding_20251016.csv');
$filePath = realpath(__DIR__ . '/csv/inspeksi/mig_pembanding_2025.csv');
if (!$filePath) {
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/inspeksi/pembanding_20251016.csv');
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/inspeksi/mig_pembanding_2025.csv');
$this->command->error('File csv tidak ditemukan.');
return;
}
@@ -33,26 +33,26 @@ class MigrationPembandingSeeder extends Seeder
return;
}
$header = fgetcsv($handle, 0, ',');
$header = fgetcsv($handle, 0, '~');
$rows = [];
$batchSize = 300;
$batchSize = 500;
$userData = [];
$branchCache = []; // <-- Gunakan sebagai cache
$totalData = 0;
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
$totalData++;
}
rewind($handle);
fgetcsv($handle, 0, ','); // Skip header
fgetcsv($handle, 0, '~'); // Skip header
$batchCount = 0;
$currentRow = 0;
$errorCount = 0;
$errorDebitureIds = [];
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
continue;
@@ -85,6 +85,8 @@ class MigrationPembandingSeeder extends Seeder
// Kelompokkan berdasarkan mig_nomor_lpj
$groupedRows = $this->groupRowsByJaminan($rows);
print_r($groupedRows);
foreach ($groupedRows as $nomorLpj => $groupRows) {
try {
// Dapatkan info permohonan untuk update inspeksi
@@ -99,10 +101,12 @@ class MigrationPembandingSeeder extends Seeder
// Bangun JSON data pembanding
$pembandingJson = $this->checkPembanding($groupRows);
print_r($pembandingJson);
// Update ke tabel inspeksi
// print_r($pembandingJson);
$inspeksi = Inspeksi::where('permohonan_id', $nomorRegis['id'])
Inspeksi::where('permohonan_id', $nomorRegis['id'])
->where('dokument_id', $nomorRegis['dokument_id'])
->update([
'data_pembanding' => $pembandingJson,
@@ -131,10 +135,6 @@ class MigrationPembandingSeeder extends Seeder
}
}
if($row['mig_nomor_lpj']=='251936'){
dd($grouped);
}
return $grouped;
}

View File

@@ -22,10 +22,10 @@ class MigrationPemilikJaminanSeeder extends Seeder
*/
public function run()
{
$filePath = realpath(__DIR__ . '/csv/pemilik_20251002.csv');
$filePath = realpath(__DIR__ . '/csv/mig_dokument_dan_pemilik.csv');
if (!$filePath) {
Log::error('File CSV tidak ditemukan: ' . __DIR__ . '/csv/pemilik_20251002.csv');
Log::error('File CSV tidak ditemukan: ' . __DIR__ . '/csv/pemilik.csv');
$this->command->error('File CSV tidak ditemukan.');
return;
}
@@ -36,7 +36,7 @@ class MigrationPemilikJaminanSeeder extends Seeder
return;
}
$header = fgetcsv($handle, 0, ',');
$header = fgetcsv($handle, 0, '~');
$rows = [];
$batchSize = 100; // Ukuran batch
$branchCache = [];
@@ -47,7 +47,7 @@ class MigrationPemilikJaminanSeeder extends Seeder
$districtCache = [];
$subdistrictCache = [];
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
continue;
@@ -91,9 +91,7 @@ class MigrationPemilikJaminanSeeder extends Seeder
$subdistrictCode = $this->getSubdistrictCode($row['mig_village_name'], $subdistrictCache, $districtCache);
PemilikJaminan::updateOrCreate([
'mig_kd_debitur_seq' => $row['mig_kd_debitur_seq'],
],[
PemilikJaminan::create([
'debiture_id' => $debitureId,
'hubungan_pemilik_jaminan_id' => $hubunganPemilik,
'name' => $row['name'],
@@ -256,25 +254,9 @@ class MigrationPemilikJaminanSeeder extends Seeder
private function parseTimestamp(?string $timestamp): ?string
{
try {
if ($timestamp) {
// Cek jika format hanya tanggal (Y-m-d)
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('Y-m-d', $timestamp)
->startOfDay()
->toDateTimeString();
}
// Format lengkap (Y-m-d H:i:s)
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
// Format d/m/Y H:i:s (contoh: 28/4/2017 14:43:43)
if (preg_match('/^\d{1,2}\/\d{1,2}\/\d{4} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $timestamp)->toDateTimeString();
}
}
return null;
return $timestamp ? \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString() : null;
} catch (\Exception $e) {
Log::error('Gagal memparsing timestamp: ' . $timestamp . '. Pesan: ' . $e->getMessage());
Log::error('Gagal memparsing timestamp: ' . $timestamp);
return null;
}
}

View File

@@ -7,6 +7,7 @@ use Modules\Lpj\Models\DokumenJaminan;
use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\Penilai;
use Modules\Lpj\Models\Laporan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class MigrationPenilaiSeeder extends Seeder
@@ -14,15 +15,15 @@ class MigrationPenilaiSeeder extends Seeder
/**
* Run the database seeds.
*/
protected $errorLogFile = __DIR__ . '/csv/penilaian/penilai.fix.latest_error.csv';
protected $errorLogFile = __DIR__ . '/csv/penilai-laporan/mig_penilai_error.csv';
public function run()
{
$this->initializeErrorLog();
// Path ke file csv
$filePath = realpath(__DIR__ . '/csv/penilaian/penilai.fix.latest_20251011.csv');
$filePath = realpath(__DIR__ . '/csv/penilai-laporan/mig_penilai.csv');
if (!$filePath) {
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/penilaian/penilai.fix.latest_20251011.csv');
Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/penilai-laporan/mig_penilai.csv');
$this->command->error('File csv tidak ditemukan.');
return;
}
@@ -33,26 +34,26 @@ class MigrationPenilaiSeeder extends Seeder
return;
}
$header = fgetcsv($handle, 0, ',');
$header = fgetcsv($handle, 0, '~');
$rows = [];
$batchSize = 500;
$userData = [];
$branchCache = []; // <-- Gunakan sebagai cache
$totalData = 0;
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
$totalData++;
}
rewind($handle);
fgetcsv($handle, 0, ','); // Skip header
fgetcsv($handle, 0, '~'); // Skip header
$batchCount = 0;
$currentRow = 0;
$errorCount = 0;
$errorDebitureIds = [];
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
Log::warning('Baris CSV memiliki jumlah kolom yang tidak sesuai: ' . json_encode($data));
continue;
@@ -94,19 +95,18 @@ class MigrationPenilaiSeeder extends Seeder
Log::warning("Nomor registrasi tidak ditemukan untuk nomor LPJ: {$nomorLpj}");
$errorCount++;
$errorDebitureIds[] = $nomorLpj;
//continue;
continue;
}
// Dapatkan type_penilai (misal: standar, sederhana)
$firstRow = reset($groupRows);
$typePenilai = $this->checkTypePenilai($firstRow['mig_kode_jenis_laporan'] ?? '');
if (!$typePenilai) {
Log::warning("Tidak ada jenis laporan valid untuk nomor LPJ: {$nomorLpj}");
$errorCount++;
$errorDebitureIds[] = $nomorLpj;
//continue;
continue;
}
// Bangun JSON type_penilai
@@ -132,7 +132,7 @@ class MigrationPenilaiSeeder extends Seeder
Log::warning("Field tidak dikenali untuk tipe: {$typePenilai}");
$errorCount++;
$errorDebitureIds[] = $nomorLpj;
//continue;
continue;
}
// NO: 001/241917/LPJ/PJ-2251/VII/24
@@ -144,13 +144,12 @@ class MigrationPenilaiSeeder extends Seeder
// 24 => tahun
// Generate nomor_laporan
$mig_permohonan = json_decode($nomorRegis['mig_permohonan'],true) ?? '';
$tanggal = $mig_permohonan['mig_mst_lpj_tgl_laporan'] ?? $mig_permohonan['mig_mst_lpj_tgl_oto'] ?? $firstRow['mig_created_at'];
//$tanggal = $firstRow['mig_created_at'];
$tanggal = $firstRow['mig_crated_at'];
$nomorD = $nomorRegis['nomor_registrasi'];
$nomorRegistrasi = "PJ-{$nomorD}";
$nomorLaporan = $this->generateNomorLaporan($typePenilai, $nomorLpj, $nomorRegistrasi, $tanggal, $nomorRegis['id']);
$nomorLaporan = $this->generateNomorLaporan($typePenilai, $nomorLpj, $nomorRegistrasi, $tanggal);
// Simpan atau update ke tabel Penilai
$penilaiLP = Penilai::updateOrCreate(
[
@@ -180,6 +179,8 @@ class MigrationPenilaiSeeder extends Seeder
);
$this->command->info("Berhasil simpan data penilai untuk nomor LPJ: {$nomorLpj}");
} catch (\Exception $e) {
@@ -210,8 +211,7 @@ class MigrationPenilaiSeeder extends Seeder
$result = [
'id' => $permohonan->id,
'dokument_id' => $dokumenJaminan ? $dokumenJaminan->id : null,
'nomor_registrasi' => $permohonan->nomor_registrasi,
'mig_permohonan' => $permohonan->mig_permohonan
'nomor_registrasi' => $permohonan->nomor_registrasi
];
$cache[$nomorLpj] = $result;
@@ -260,7 +260,6 @@ class MigrationPenilaiSeeder extends Seeder
// Inisialisasi struktur JSON
$penilaiJson = [
'luas_bangunan' => null,
'luas_tanah' => null,
'nilai_tanah_1' => null,
'nilai_tanah_2' => null,
@@ -320,6 +319,11 @@ class MigrationPenilaiSeeder extends Seeder
// Proses tambahan (urutan > 1)
foreach ($groupRows as $row) {
// Skip baris dengan urutan_seq = 1 karena sudah diproses
if (($row['mig_urutan_seq'] ?? '') == 1) {
continue;
}
// Hanya proses jika mig_urutan_seq ada
$urutan = $row['mig_urutan_seq'] ?? '';
if (empty($urutan)) {
@@ -368,8 +372,7 @@ class MigrationPenilaiSeeder extends Seeder
];
// Dapatkan tahun dan bulan dari tanggal
$tanggal_ = $this->parseTimestamp($tanggal);
$date = \Carbon\Carbon::parse($tanggal_);
$date = \Carbon\Carbon::parse($tanggal);
$kodeCabang = '001'; // bisa diambil dari user atau parameter lain jika dinamis
$tahun = substr($date->year, -2); // 2024 → 24
$bulan = $this->convertToRoman($date->month); // 7 → VII
@@ -390,95 +393,22 @@ class MigrationPenilaiSeeder extends Seeder
private function parseTimestamp(?string $timestamp): ?string
{
if (!$timestamp) {
try {
if ($timestamp) {
// Cek jika format hanya tanggal (Y-m-d)
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('Y-m-d', $timestamp)
->startOfDay()
->toDateTimeString();
}
// Format lengkap (Y-m-d H:i:s)
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
return null;
} catch (\Exception $e) {
Log::error('Gagal memparsing timestamp: ' . $timestamp . '. Pesan: ' . $e->getMessage());
return null;
}
// Trim whitespace dan normalize
$timestamp = trim($timestamp);
// Log untuk debugging
Log::info('Mencoba parsing timestamp: "' . $timestamp . '"');
// Parsing dengan DateTime native PHP untuk lebih robust
try {
// Pattern untuk format d/m/Y H:i:s
if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/', $timestamp, $matches)) {
$day = (int) $matches[1];
$month = (int) $matches[2];
$year = (int) $matches[3];
$hour = (int) $matches[4];
$minute = (int) $matches[5];
$second = (int) $matches[6];
// Validasi nilai
if ($day >= 1 && $day <= 31 && $month >= 1 && $month <= 12 && $year >= 1900 && $year <= 2100 &&
$hour >= 0 && $hour <= 23 && $minute >= 0 && $minute <= 59 && $second >= 0 && $second <= 59) {
// Buat DateTime object langsung
$dateTime = new \DateTime();
$dateTime->setDate($year, $month, $day);
$dateTime->setTime($hour, $minute, $second);
$result = $dateTime->format('Y-m-d H:i:s');
Log::info('Berhasil parsing dengan DateTime: ' . $timestamp . ' -> ' . $result);
return $result;
}
}
// Pattern untuk format d/m/Y tanpa waktu
if (preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/', $timestamp, $matches)) {
$day = (int) $matches[1];
$month = (int) $matches[2];
$year = (int) $matches[3];
// Validasi nilai
if ($day >= 1 && $day <= 31 && $month >= 1 && $month <= 12 && $year >= 1900 && $year <= 2100) {
// Buat DateTime object langsung
$dateTime = new \DateTime();
$dateTime->setDate($year, $month, $day);
$dateTime->setTime(0, 0, 0);
$result = $dateTime->format('Y-m-d H:i:s');
Log::info('Berhasil parsing tanpa waktu dengan DateTime: ' . $timestamp . ' -> ' . $result);
return $result;
}
}
} catch (\Exception $e) {
Log::error('Gagal parsing dengan DateTime: ' . $timestamp . '. Error: ' . $e->getMessage());
}
// Fallback ke format Carbon standar untuk format lainnya
$formats = [
'Y-m-d H:i:s',
'Y-m-d',
'd-m-Y H:i:s',
'd-m-Y',
'j-n-Y H:i:s',
'j-n-Y',
];
foreach ($formats as $format) {
try {
$carbon = \Carbon\Carbon::createFromFormat($format, $timestamp);
if ($carbon && $carbon->format($format) === $timestamp) {
// Jika format tidak mengandung waktu, set ke awal hari
if (!str_contains($format, 'H:i:s')) {
$carbon = $carbon->startOfDay();
}
Log::info('Berhasil parsing dengan format ' . $format . ': ' . $timestamp . ' -> ' . $carbon->toDateTimeString());
return $carbon->toDateTimeString();
}
} catch (\Exception $e) {
// Lanjut ke format berikutnya
continue;
}
}
Log::error('Tidak dapat memparsing timestamp dengan format apapun: "' . $timestamp . '"');
return null;
}
private function initializeErrorLog()

View File

@@ -12,17 +12,17 @@ use Modules\Usermanagement\Models\User;
class MigrationPermohonanSeeder extends Seeder
{
protected $errorLogFile = __DIR__ . '/csv/permohonan/permohonan.2024.2025_error.csv';
protected $errorLogFile = __DIR__ . '/csv/permohonan/mig_permohonan_extenal_error.csv';
public function run()
{
// Bersihkan file error sebelum mulai
$this->initializeErrorLog();
$filePath = realpath(__DIR__ . '/csv/permohonan/permohonan.2024.2025.csv');
$filePath = realpath(__DIR__ . '/csv/permohonan/mig_permohonan_external.csv');
if (!$filePath) {
Log::error('File CSV tidak ditemukan: ' . __DIR__ . '/csv/permohonan/permohonan.2024.2025.csv');
Log::error('File CSV tidak ditemukan: ' . __DIR__ . '/csv/permohonan/mig_permohonan_external.csv');
$this->command->error('File CSV tidak ditemukan.');
return;
}
@@ -33,7 +33,7 @@ class MigrationPermohonanSeeder extends Seeder
return;
}
$header = fgetcsv($handle, 0, ',');
$header = fgetcsv($handle, 0, '~');
$rows = [];
$batchSize = 1000;
$branchCache = [];
@@ -43,17 +43,17 @@ class MigrationPermohonanSeeder extends Seeder
$batchCount = 0;
// Hitung total data
while (fgetcsv($handle, 0, ',') !== false) {
while (fgetcsv($handle, 0, '~') !== false) {
$totalData++;
}
rewind($handle);
fgetcsv($handle, 0, ','); // Lewati header
fgetcsv($handle, 0, '~'); // Lewati header
while (($data = fgetcsv($handle, 0, ',')) !== false) {
while (($data = fgetcsv($handle, 0, '~')) !== false) {
if (count($data) != count($header)) {
$nomorJaminan = $data[array_search('mig_mst_jaminan_nomor_jaminan', $header)] ?? '-';
$this->logError($nomorJaminan, count($data).' Jumlah kolom tidak sesuai dengan header. Header: '.count($header));
$this->logError($nomorJaminan, 'Jumlah kolom tidak sesuai');
continue;
}
@@ -92,7 +92,7 @@ class MigrationPermohonanSeeder extends Seeder
$existingRecord = Permohonan::where('nomor_registrasi', $row['mig_mst_jaminan_nomor_jaminan'])->first();
if ($existingRecord && $existingRecord->processed_at) {
$this->command->info("Data sudah diproses: $nomorJaminan");
//continue;
continue;
}
// Ambil branch_id
@@ -113,9 +113,8 @@ class MigrationPermohonanSeeder extends Seeder
$userId = $this->getUserId($row['mig_mst_jaminan_nama_ao'], $branchCache, true);
// jika external matikan
$approved1Id = $this->getUserId($row['mig_mst_lpj_user_approved_1'], $branchCache, false);
$approved2Id = $this->getUserId($row['mig_mst_lpj_user_approved_2'], $branchCache, false);
$periksaId = $this->getUserId($row['mig_mst_lpj_user_periksa'], $branchCache, false);
// $approved1Id = $this->getUserId($row['mig_mst_lpj_user_approved_1'], $branchCache, false);
// $approved2Id = $this->getUserId($row['mig_mst_lpj_user_approved_2'], $branchCache, false);
// // Ambil user IDs berdasarkan NIK
$userIdUpdate = $this->getUserIdData($row['mig_mst_jaminan_user_create'] ?? null, $userData)['id'];
@@ -152,9 +151,7 @@ class MigrationPermohonanSeeder extends Seeder
$jenisPenilaian = $row['mig_internal_or_external'] == 1 ? 2 : 1;
// Simpan data
$permohonan = Permohonan::updateOrCreate([
'nomor_registrasi' => $nomorJaminan,
],[
Permohonan::create([
'nomor_registrasi' => $nomorJaminan,
'tanggal_permohonan' => $this->parseDate($row['tanggal_permohonan']),
'user_id' => $userId['id'],
@@ -165,12 +162,12 @@ class MigrationPermohonanSeeder extends Seeder
'nilai_plafond_id' => 2,
'status' => 'done',
// jika external matikan
'approval_eo' => $approved1Id['id'] ?? 0,
'approval_eo_at' => $this->parseTimestamp($row['mig_mst_lpj_tgl_approved_1']),
'approval_dd' => $approved2Id['id'] ?? 0,
'approval_dd_at' => $this->parseTimestamp($row['mig_mst_lpj_tgl_approved_2']),
'approval_so' => $periksaId['id'] ?? 0,
'approval_so_at' => $this->parseTimestamp($row['mig_mst_lpj_tgl_periksa']),
// 'approval_eo' => $approved2Id['id'] ?? 0,
// 'approval_eo_at' => $this->parseTimestamp($row['mig_mst_lpj_tgl_approved_2']),
// 'approval_dd' => 0,
// 'approval_dd_at' => null,
// 'approval_so' => $approved1Id['id'] ?? 0,
// 'approval_so_at' => $this->parseTimestamp($row['mig_mst_lpj_tgl_approved_1']),
// end external matikan
'keterangan' => $row['mig_mst_jaminan_catatan'] ?? null,
'status_bayar' => 'sudah_bayar',
@@ -184,20 +181,16 @@ class MigrationPermohonanSeeder extends Seeder
'created_by' => $mapUser['created_by'],
'updated_by' => $mapUser['updated_by'],
'mig_nama_ao' => $row['mig_mst_jaminan_nama_ao'],
'mig_permohonan' => json_encode($row),
'is_mig' => 1
]);
if($permohonan && $nomorJaminan=='253122'){
Log::info($permohonan);
}
$this->command->info("Proses data permohonan $nomorJaminan (" . ($index + 1) . '/' . count($rows) . " pada batch ke-$batchCount)");
} catch (\Exception $e) {
$nomorJaminan = $row['mig_mst_jaminan_nomor_jaminan'] ?? '-';
$this->logError($nomorJaminan, "Error eksepsi: " . $e->getMessage());
continue;
}
}
}
@@ -284,9 +277,6 @@ class MigrationPermohonanSeeder extends Seeder
}
private function checkJenisFasilitas($code): int
{
// Extract the integer part before the decimal
$intCode = (int) $code;
$mapping = [
161337594516 => 1,
161337598118 => 14,
@@ -298,12 +288,11 @@ class MigrationPermohonanSeeder extends Seeder
1 => 13,
];
return $mapping[$intCode] ?? 0;
return $mapping[$code] ?? 0;
}
private function checkTujuanPenilaian($code): int
{
$code = (int) $code;
$mapping = [
1 => 1,
2 => 2,
@@ -331,54 +320,27 @@ class MigrationPermohonanSeeder extends Seeder
private function parseDate(?string $date): ?string
{
if (!$date) {
return null;
}
try {
// Coba format d/m/Y terlebih dahulu (contoh: 30/1/2025)
return \Carbon\Carbon::createFromFormat('d/m/Y', $date)->toDateString();
return $date ? \Carbon\Carbon::createFromFormat('Y-m-d', $date)->toDateString() : null;
} catch (\Exception $e) {
try {
// Fallback ke format Y-m-d jika gagal
return \Carbon\Carbon::createFromFormat('Y-m-d', $date)->toDateString();
} catch (\Exception $e2) {
// If both formats fail, try to parse with Carbon::parse as last resort
try {
return \Carbon\Carbon::parse($date)->toDateString();
} catch (\Exception $e3) {
return null;
}
}
return null;
}
}
private function parseTimestamp(?string $timestamp): ?string
{
try {
if (!$timestamp) {
return null;
}
// Cek format d/m/Y H:i:s (contoh: 29/9/2025 17:20:36)
if (preg_match('/^\d{1,2}\/\d{1,2}\/\d{4} \d{1,2}:\d{2}:\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('d/m/Y H:i:s', $timestamp)->toDateTimeString();
}
// Cek jika format hanya tanggal (Y-m-d)
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('Y-m-d', $timestamp)
->startOfDay()
->toDateTimeString();
}
// Format lengkap (Y-m-d H:i:s)
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
if ($timestamp) {
// Cek jika format hanya tanggal (Y-m-d)
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $timestamp)) {
return \Carbon\Carbon::createFromFormat('Y-m-d', $timestamp)
->startOfDay()
->toDateTimeString();
}
// Format lengkap (Y-m-d H:i:s)
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $timestamp)->toDateTimeString();
}
// Fallback: coba parsing dengan createFromFormat tambahan atau gunakan parse
return \Carbon\Carbon::parse($timestamp)->toDateTimeString();
return null;
} catch (\Exception $e) {
Log::error('Gagal memparsing timestamp: ' . $timestamp . '. Pesan: ' . $e->getMessage());
return null;

View File

@@ -0,0 +1,16 @@
<?php
namespace Modules\Lpj\Database\Seeders;
use Illuminate\Database\Seeder;
class PosisiKavlingSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// $this->call([]);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Modules\Lpj\Database\Seeders;
use Illuminate\Database\Seeder;
class PosisiUnitSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// $this->call([]);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Modules\Lpj\Database\Seeders;
use Illuminate\Database\Seeder;
class SaranaPelengkapSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// $this->call([]);
}
}

View File

@@ -13,21 +13,6 @@
],
"menu": {
"laporan": [
{
"title": "Laporan Penilaian Jaminan",
"path": "laporan",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo",
"DD Appraisal",
"EO Appraisal"
]
},
{
"title": "Laporan Permohonan",
"path": "laporan-permohonan",
@@ -62,6 +47,58 @@
"senior-officer"
]
},
{
"title": "Laporan Admin Kredit",
"path": "laporan-admin-kredit",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"adk",
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
]
},
{
"title": "Laporan Penilai Jaminan",
"path": "laporan-penilai-jaminan",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
]
},
{
"title": "Laporan Pernilaan Jaminan",
"path": "laporan-penilaian-jaminan",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
]
},
{
"title": "Laporan Hasil Pernilaan Jaminan Internal & External",
"path": "laporan-hasil-penilaian-jaminan-internal-external",
@@ -71,6 +108,8 @@
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
@@ -78,7 +117,7 @@
]
},
{
"title": "Rekap Harian SO",
"title": "Rekap Harian So",
"path": "rekap-harian-so",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
@@ -88,16 +127,17 @@
"administrator",
"senior-officer"
]
},
{
"title": "Laporan Biaya LPJ",
}, {
"title": "Laporan Biaya Lpj",
"path": "laporan-biaya",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
@@ -112,11 +152,13 @@
"attributes": [],
"permission": "",
"roles": [
"administrator",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
]
},
{
@@ -127,16 +169,17 @@
"attributes": [],
"permission": "",
"roles": [
"administrator",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
]
}
]
},
{
},{
"title": "Laporan Debitur",
"path": "laporan-debiture",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
@@ -161,7 +204,7 @@
]
},
{
"title": "Laporan Monitoring SO",
"title": "Laporan Monitoring so",
"path": "laporan-monitoring",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
@@ -181,6 +224,8 @@
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
@@ -210,6 +255,7 @@
"permission": "",
"roles": [
"administrator",
"senior-officer",
"pemohon-eo"
]
},
@@ -222,6 +268,7 @@
"permission": "",
"roles": [
"administrator",
"admin",
"EO Appraisal",
"DD Appraisal"
]
@@ -235,6 +282,7 @@
"permission": "",
"roles": [
"administrator",
"admin",
"pemohon-eo"
]
},
@@ -290,26 +338,7 @@
"attributes": [],
"permission": "",
"roles": [
"administrator",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer",
"penilai",
"surveyor"
]
},
{
"title": "Data Permohonan",
"path": "debitur",
"icon": "ki-filled ki-people text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"admin"
"administrator"
]
},
{
@@ -322,83 +351,7 @@
"roles": [
"administrator",
"pemohon-ao",
"admin"
]
},
{
"title": "Persetujuan Penawaran KJPP",
"path": "persetujuan-penawaran",
"icon": "ki-filled ki-double-check text-lg text-info",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao"
]
},
{
"title": "Biaya Appraisal",
"path": "pembayaran",
"icon": "ki-filled ki-credit-cart text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao"
],
"sub": [
{
"title": "Pembayaran",
"path": "pembayaran",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao"
]
},
{
"title": "Memo Penyelesaian",
"path": "memo",
"icon": "ki-filled ki-document text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
]
},
{
"title": "Kurang Bayar",
"path": "pembayaran.kurang",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao"
]
},
{
"title": "Lebih Bayar",
"path": "pembayaran.lebih",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao"
]
}
"pemohon-eo"
]
},
{
@@ -410,60 +363,19 @@
"permission": "",
"roles": [
"administrator",
"pemohon-ao"
"pemohon-eo"
]
},
{
"title": "Admin Kredit",
"path": "admin-kredit",
"icon": "ki-filled ki-briefcase text-lg text-primary",
"title": "Persetujuan Penawaran",
"path": "persetujuan-penawaran",
"icon": "ki-filled ki-double-check text-lg text-info",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"adk"
],
"sub": [
{
"title": "SLIK",
"path": "admin-kredit.slik",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"adk",
"administrator",
"admin"
]
},
{
"title": "Laporan Admin Kredit",
"path": "admin-kredit.laporan",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"adk",
"administrator",
"admin"
]
},
{
"title": "Laporan SLIK",
"path": "admin-kredit.laporan-slik",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"adk",
"administrator",
"admin"
]
}
"pemohon-ao"
]
},
{
@@ -475,11 +387,11 @@
"permission": "",
"roles": [
"administrator",
"noc"
"admin"
],
"sub": [
{
"title": "Pembukuan",
"title": "Pembayaran",
"path": "noc.pembayaran",
"icon": "ki-filled ki-two-credit-cart text-lg text-primary",
"classes": "",
@@ -499,20 +411,6 @@
"permission": "",
"roles": [
"administrator",
"admin",
"noc"
]
},
{
"title": "Bucok",
"path": "bucok",
"icon": "ki-filled ki-two-credit-cart text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"admin",
"noc"
]
}
@@ -531,7 +429,7 @@
]
},
{
"title": "Permohonan KJPP",
"title": "Tender",
"path": "tender",
"icon": "ki-filled ki-briefcase text-lg text-primary",
"classes": "",
@@ -563,45 +461,70 @@
"administrator",
"admin"
]
},
{
"title": "SPK",
"path": "spk",
"icon": "ki-filled ki-document text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"admin"
]
},
{
"title": "Registrasi Final",
"path": "registrasifinal",
"icon": "ki-filled ki-file-added text-lg text-success",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"admin"
]
},
{
"title": "Laporan External",
"path": "laporan-external",
"icon": "ki-filled ki-document text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"admin"
]
}
]
},
{
"title": "SPK",
"path": "spk",
"icon": "ki-filled ki-document text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"admin"
]
},
{
"title": "Laporan External",
"path": "laporan-external",
"icon": "ki-filled ki-document text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"admin"
]
},
{
"title": "Registrasi Final",
"path": "registrasifinal",
"icon": "ki-filled ki-file-added text-lg text-success",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"admin"
]
},
{
"title": "Pembayaran",
"path": "pembayaran",
"icon": "ki-filled ki-credit-cart text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao"
]
},
{
"title": "Data Debitur",
"path": "debitur",
"icon": "ki-filled ki-people text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo"
]
},
{
"title": "Assignment",
"path": "penilaian",
@@ -635,8 +558,7 @@
"permission": "",
"roles": [
"administrator",
"surveyor",
"penilai"
"surveyor"
]
},
{
@@ -648,9 +570,10 @@
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"surveyor",
"penilai",
"senior-officer",
"EO Appraisal",
"DD Appraisal"
@@ -665,9 +588,42 @@
"permission": "",
"roles": [
"administrator",
"penilai",
"surveyor"
]
},
{
"title": "Resume dan Laporan",
"path": "laporan",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
]
},
{
"title": "Daftar Pustaka",
"path": "daftar-pustaka",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
]
}
],
"master": [
@@ -680,8 +636,7 @@
"permission": "",
"roles": [
"administrator",
"admin",
"senior-officer"
"admin"
],
"sub": [
{
@@ -813,8 +768,7 @@
"permission": "",
"roles": [
"administrator",
"admin",
"senior-officer"
"admin"
]
},
{
@@ -1227,24 +1181,6 @@
]
}
],
"system": [
{
"title": "Daftar Pustaka",
"path": "daftar-pustaka",
"icon": "ki-filled ki-filter-tablet text-lg text-primary",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"pemohon-ao",
"pemohon-eo",
"admin",
"DD Appraisal",
"EO Appraisal",
"senior-officer"
]
}
]
"system": []
}
}

View File

@@ -31,27 +31,5 @@ function hitungHariKerja(tanggalMulai, tanggalSelesai) {
return hariKerja;
}
function showLoadingSwal(message, duration = 5000) {
Swal.fire({
title: message,
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
},
timer: duration, // Durasi dalam milidetik
timerProgressBar: true, // Menampilkan progres bar timer
}).then((result) => {
if (result.dismiss === Swal.DismissReason.timer) {
console.log("Dialog loading otomatis ditutup.");
}
});
}
function hideLoadingSwal() {
Swal.close();
}
// Make the function available globally
window.hitungHariKerja = hitungHariKerja;
window.showLoadingSwal = showLoadingSwal;
window.hideLoadingSwal = hideLoadingSwal;

View File

@@ -43,15 +43,15 @@
}
</style>
@endpush
<div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<div class="pb-2.5 border card border-agi-100">
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<div class="card border border-agi-100 pb-2.5">
<div class="card-header bg-agi-50" id="basic_settings">
<div class="flex flex-row gap-1.5 card-title">
<div class="card-title flex flex-row gap-1.5">
Activity
</div>
<div class="flex-wrap py-5 card-header bg-agi-50">
<div class="card-header bg-agi-50 py-5 flex-wrap">
<div class="flex flex-wrap gap-2 lg:gap-5">
<div class="flex">
@@ -64,30 +64,30 @@
<div class="dropdown" data-dropdown="true" data-dropdown-trigger="click">
<button
class="inline-flex justify-between items-center w-full dropdowns-toggle btn btn-sm btn-light">
class="dropdowns-toggle btn btn-sm btn-light inline-flex justify-between w-full items-center">
Pilih Status
<i class="ki-outline ki-down dropdown-open:hidden">
</i>
<i class="hidden ki-outline ki-up dropdown-open:block">
<i class="ki-outline ki-up hidden dropdown-open:block">
</i>
</button>
<div
class="absolute z-50 py-2 mt-2 w-full bg-white rounded-md ring-1 ring-black ring-opacity-5 shadow-lg origin-top-right dropdowns-content max-w-56">
<div class="flex flex-col w-full menu menu-default">
class="dropdowns-content w-full max-w-56 py-2 absolute mt-2 origin-top-right z-50 bg-white rounded-md shadow-lg ring-1 ring-black ring-opacity-5">
<div class="menu menu-default flex flex-col w-full">
<!-- Checkbox untuk All Status -->
<div class="menu-item">
<label class="flex items-center px-4 py-2 text-sm text-gray-700 menu-link">
<label class="menu-link flex items-center px-4 py-2 text-sm text-gray-700">
<input id="select-all" type="checkbox"
class="w-4 h-4 text-blue-600 form-checkbox">
class="form-checkbox h-4 w-4 text-blue-600">
<span class="ml-2">All Status</span>
</label>
</div>
<!-- Dinamis Status dari Backend -->
@foreach ($status_permohonan as $item)
<div class="menu-item">
<label class="flex items-center px-4 py-2 text-sm text-gray-700 menu-link">
<label class="menu-link flex items-center px-4 py-2 text-sm text-gray-700">
<input type="checkbox"
class="w-4 h-4 text-blue-600 form-checkbox status-checkbox"
class="form-checkbox status-checkbox h-4 w-4 text-blue-600"
value="{{ strtolower($item->name) }}">
<span class="ml-2">{{ $item->name }}</span>
</label>
@@ -105,12 +105,12 @@
</div>
</div>
<div class="min-w-full card-grid" data-datatable="false" data-datatable-page-size="10"
<div class="card-grid min-w-full" data-datatable="false" data-datatable-page-size="10"
data-datatable-state-save="false" id="permohonan-table" data-api-url="{{ route('activity.datatables') }}">
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm"
data-datatable-table="true">
<thead>
<tr>
@@ -158,13 +158,13 @@
</table>
</div>
<div
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
<div class="flex items-center gap-2">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per
<select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per
page
</div>
<div class="flex gap-4 items-center">
<div class="flex items-center gap-4">
<span data-datatable-info="true"></span>
<div class="pagination" data-datatable-pagination="true"></div>
</div>
@@ -261,10 +261,10 @@
},
},
actions: {
title: 'Action',
render: (item, data) => {
const status = data.status; // Anggap status berada di dalam objek data
const dokumenjaminan = data.dokumenjaminan || [];
title: 'Action',
render: (item, data) => {
const status = data.status; // Anggap status berada di dalam objek data
const dokumenjaminan = data.dokumenjaminan || [];
return `
<div class="flex flex-nowrap justify-center">
@@ -291,6 +291,8 @@
};
let dataTable = new KTDataTable(element, dataTableOptions);
function highlightFreezeRows() {

View File

@@ -1,342 +0,0 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render('bucok') }}
@endsection
@section('content')
<div class="grid">
<div class="min-w-full border card border-agi-100 card-grid" data-datatable="false" data-datatable-page-size="10" data-datatable-state-save="false" id="bucok-table" data-api-url="{{ route('bucok.datatables') }}">
<div class="flex-wrap py-5 card-header bg-agi-50">
<h3 class="card-title">
Data Bucok
</h3>
<div class="flex flex-wrap gap-2 lg:gap-5">
<div class="flex">
<label class="input input-sm">
<i class="ki-filled ki-magnifier"></i>
<input placeholder="Search Bucok" id="search" type="text" value="">
</label>
</div>
<div class="flex flex-wrap gap-2.5">
<div class="h-[24px] border border-r-gray-200"></div>
<!-- Filter Tahun -->
<select class="select select-sm" id="year-filter">
<option value="">Semua Tahun</option>
@for($year = date('Y'); $year >= 2020; $year--)
<option value="{{ $year }}">{{ $year }}</option>
@endfor
</select>
<!-- Filter Bulan -->
<select class="select select-sm" id="month-filter">
<option value="">Semua Bulan</option>
@for($month = 1; $month <= 12; $month++)
<option value="{{ $month }}">{{ DateTime::createFromFormat('!m', $month)->format('F') }}</option>
@endfor
</select>
<!-- Filter Status Penyelesaian -->
<select class="select select-sm" id="completion-status-filter">
<option value="">Semua Status</option>
<option value="1">Selesai</option>
<option value="0">Belum Selesai</option>
</select>
<!-- Import Excel -->
<button class="btn btn-sm btn-success" data-modal-toggle="#import-modal">
<i class="ki-filled ki-file-up"></i> Import Excel
</button>
<a class="btn btn-sm btn-light" href="#" id="export-excel">
<i class="ki-filled ki-file-down"></i> Export Excel
</a>
</div>
</div>
</div>
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border" data-datatable-table="true">
<thead>
<tr>
<th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox"/>
</th>
<th class="min-w-[80px]" data-datatable-column="no">
<span class="sort">
<span class="sort-label">No</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[100px]" data-datatable-column="tanggal">
<span class="sort">
<span class="sort-label">Tanggal</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[120px]" data-datatable-column="nomor_tiket">
<span class="sort">
<span class="sort-label">Nomor Tiket</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[100px]" data-datatable-column="nomor_coa">
<span class="sort">
<span class="sort-label">Nomor COA</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[150px]" data-datatable-column="nama_coa">
<span class="sort">
<span class="sort-label">Nama COA</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[200px]" data-datatable-column="deskripsi">
<span class="sort">
<span class="sort-label">Deskripsi</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[120px]" data-datatable-column="nominal">
<span class="sort">
<span class="sort-label">Nominal</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[100px]" data-datatable-column="cost_center">
<span class="sort">
<span class="sort-label">Cost Center</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[100px]" data-datatable-column="status_penyelesaian">
<span class="sort">
<span class="sort-label">Status</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[50px] text-center" data-datatable-column="actions">Aksi</th>
</tr>
</thead>
</table>
</div>
<div class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per page
</div>
<div class="flex gap-4 items-center">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal Import Excel -->
<div class="modal" data-modal="true" id="import-modal">
<div class="modal-content max-w-[500px] top-[15%]">
<div class="modal-header">
<h3 class="modal-title">Import Data Bucok</h3>
<button class="btn btn-sm btn-icon btn-light btn-clear shrink-0" data-modal-dismiss="true">
<i class="ki-filled ki-cross"></i>
</button>
</div>
<form action="{{ route('bucok.import') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="modal-body">
<div class="flex flex-col gap-5">
<div class="flex flex-col gap-2.5">
<label class="text-gray-900 form-label">File Excel</label>
<input type="file" name="file" class="file-input" accept=".xlsx,.xls,.csv" required>
<div class="text-gray-600 text-2sm">
Format yang didukung: .xlsx, .xls, .csv (Maksimal 10MB)
</div>
</div>
<div class="p-3 bg-yellow-100 rounded border border-yellow-300">
<div class="text-sm text-yellow-800">
<strong>Catatan:</strong>
<ul class="mt-1 list-disc list-inside">
<li>Data akan dimulai dari baris ke-5</li>
<li>File Excel tidak perlu header</li>
<li>Data akan di-update jika nomor tiket sudah ada</li>
</ul>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="flex gap-4">
<button type="button" class="btn btn-light" data-modal-dismiss="true">Batal</button>
<button type="submit" class="btn btn-primary">Import</button>
</div>
</div>
</form>
</div>
</div>
@endsection
@push('scripts')
<script>
/**
* Fungsi untuk menghapus data Bucok
* @param {number} data - ID data yang akan dihapus
*/
function deleteData(data) {
Swal.fire({
title: 'Apakah Anda yakin?',
text: "Data yang dihapus tidak dapat dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Ya, hapus!',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
});
$.ajax(`bucok/${data}`, {
type: 'DELETE'
}).then((response) => {
Swal.fire('Terhapus!', 'Data Bucok berhasil dihapus.', 'success').then(() => {
window.location.reload();
});
}).catch((error) => {
Swal.fire('Error!', error.responseJSON.message, 'error');
});
}
})
}
</script>
<script type="module">
/**
* Inisialisasi DataTable untuk Bucok menggunakan KTDataTable
*/
document.addEventListener('DOMContentLoaded', function() {
const element = document.querySelector('#bucok-table');
const searchInput = document.getElementById('search');
const yearFilter = document.getElementById('year-filter');
const monthFilter = document.getElementById('month-filter');
const completionStatusFilter = document.getElementById('completion-status-filter');
const apiUrl = element.getAttribute('data-api-url');
// Konfigurasi DataTable menggunakan KTDataTable
const dataTableOptions = {
apiEndpoint: apiUrl,
pageSize: 10,
columns: {
select: {
render: (item, data, context) => {
const checkbox = document.createElement('input');
checkbox.className = 'checkbox checkbox-sm';
checkbox.type = 'checkbox';
checkbox.value = data.id.toString();
checkbox.setAttribute('data-datatable-row-check', 'true');
return checkbox.outerHTML.trim();
},
},
no: {
title: 'No',
},
tanggal: {
title: 'Tanggal',
},
nomor_tiket: {
title: 'Nomor Tiket',
},
nomor_coa: {
title: 'Nomor COA',
},
nama_coa: {
title: 'Nama COA',
},
deskripsi: {
title: 'Deskripsi',
render: (item, data) => {
const desc = data.deskripsi || '-';
return desc.length > 50 ? desc.substring(0, 50) + '...' : desc;
},
},
nominal: {
title: 'Nominal',
},
cost_center: {
title: 'Cost Center',
},
status_badge: {
title: 'Status',
render: (item, data) => {
return data.status_badge || '-';
},
},
actions: {
title: 'Aksi',
render: (item, data) => {
return data.actions || '';
},
}
},
};
// Inisialisasi DataTable
let dataTable = new KTDataTable(element, dataTableOptions);
dataTable.showSpinner();
// Fungsi pencarian
searchInput.addEventListener('input', function () {
const searchValue = this.value.trim();
dataTable.search(searchValue, true);
});
// Filter berdasarkan tahun
yearFilter.addEventListener('change', function() {
const yearValue = this.value;
// Implementasi filter tahun - perlu disesuaikan dengan API endpoint
dataTable.setParam('year', yearValue);
dataTable.reload();
});
// Filter berdasarkan bulan
monthFilter.addEventListener('change', function() {
const monthValue = this.value;
// Implementasi filter bulan - perlu disesuaikan dengan API endpoint
dataTable.setParam('month', monthValue);
dataTable.reload();
});
// Filter berdasarkan status penyelesaian
completionStatusFilter.addEventListener('change', function() {
const statusValue = this.value;
// Implementasi filter status - perlu disesuaikan dengan API endpoint
dataTable.setParam('completion_status', statusValue);
dataTable.reload();
});
// Export Excel functionality
document.getElementById('export-excel').addEventListener('click', function(e) {
e.preventDefault();
// Build export URL with current filters
const params = new URLSearchParams();
if (searchInput.value) params.append('search', searchInput.value);
if (yearFilter.value) params.append('year', yearFilter.value);
if (monthFilter.value) params.append('month', monthFilter.value);
if (completionStatusFilter.value) params.append('completion_status', completionStatusFilter.value);
const exportUrl = '{{ route("bucok.export") }}?' + params.toString();
window.open(exportUrl, '_blank');
});
});
</script>
@endpush

View File

@@ -1,236 +0,0 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render('bucok.show', $bucok) }}
@endsection
@section('content')
<div class="grid gap-5 lg:gap-7.5">
<!-- Header Card -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Detail Bucok #{{ $bucok->nomor_tiket }}</h3>
<div class="flex gap-2">
<a href="{{ route('bucok.index') }}" class="btn btn-sm btn-light">
<i class="ki-filled ki-left"></i> Kembali
</a>
</div>
</div>
</div>
<!-- Detail Information -->
<div class="grid gap-5 lg:grid-cols-2 lg:gap-7.5">
<!-- Informasi Dasar -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Informasi Dasar</h3>
</div>
<div class="card-body">
<div class="grid gap-5">
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">No</div>
<div class="text-gray-700 text-2sm">{{ $bucok->no ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Tanggal</div>
<div class="text-gray-700 text-2sm">
{{ $bucok->tanggal ? dateFormat($bucok->tanggal, true) : '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Bulan</div>
<div class="text-gray-700 text-2sm">{{ $bucok->bulan ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Tahun</div>
<div class="text-gray-700 text-2sm">{{ $bucok->tahun ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Nomor Tiket</div>
<div class="font-medium text-gray-700 text-2sm">{{ $bucok->nomor_tiket }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Status Penyelesaian</div>
<div class="text-2sm">{!! $bucok->status_badge !!}</div>
</div>
</div>
</div>
</div>
</div>
<!-- Informasi COA -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Informasi COA</h3>
</div>
<div class="card-body">
<div class="grid gap-5">
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Nomor COA</div>
<div class="text-gray-700 text-2sm">{{ $bucok->nomor_coa ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Nama COA</div>
<div class="text-gray-700 text-2sm">{{ $bucok->nama_coa ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Deskripsi</div>
<div class="text-gray-700 text-2sm">{{ $bucok->deskripsi ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Nominal</div>
<div class="font-medium text-gray-700 text-2sm">{{ $bucok->nominal_formatted }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Informasi Organisasi -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Informasi Organisasi</h3>
</div>
<div class="card-body">
<div class="grid gap-5 lg:grid-cols-3">
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Cost Center</div>
<div class="text-gray-700 text-2sm">{{ $bucok->cost_center ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Sub Direktorat</div>
<div class="text-gray-700 text-2sm">{{ $bucok->nama_sub_direktorat ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Direktorat/Cabang</div>
<div class="text-gray-700 text-2sm">{{ $bucok->nama_direktorat_cabang ?? '-' }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- Informasi Penyelesaian -->
@if ($bucok->tanggal_penyelesaian || $bucok->nominal_penyelesaian || $bucok->penyelesaian)
<div class="card">
<div class="card-header">
<h3 class="card-title">Informasi Penyelesaian</h3>
</div>
<div class="card-body">
<div class="grid gap-5 lg:grid-cols-2">
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Tanggal Penyelesaian</div>
<div class="text-gray-700 text-2sm">
{{ $bucok->tanggal_penyelesaian ? dateFormat($bucok->tanggal_penyelesaian, true) : '-' }}
</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Nominal Penyelesaian</div>
<div class="font-medium text-gray-700 text-2sm">
{{ $bucok->nominal_penyelesaian_formatted }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Penyelesaian</div>
<div class="text-gray-700 text-2sm">{{ $bucok->penyelesaian ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Umur Aging</div>
<div class="text-gray-700 text-2sm">{{ $bucok->umur_aging ?? '-' }} hari</div>
</div>
</div>
@if ($bucok->keterangan_gantung)
<div class="flex justify-between items-center py-2.5">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Keterangan Gantung</div>
<div class="text-gray-700 text-2sm">{{ $bucok->keterangan_gantung }}</div>
</div>
</div>
@endif
</div>
</div>
</div>
@endif
<!-- Informasi Audit -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Informasi Audit</h3>
</div>
<div class="card-body">
<div class="grid gap-5 lg:grid-cols-2">
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Dibuat Oleh</div>
<div class="text-gray-700 text-2sm">{{ $bucok->creator?->name ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Tanggal Dibuat</div>
<div class="text-gray-700 text-2sm">{{ dateFormat($bucok->created_at, true) }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5 border-b border-gray-200">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Diperbarui Oleh</div>
<div class="text-gray-700 text-2sm">{{ $bucok->updater?->name ?? '-' }}</div>
</div>
</div>
<div class="flex justify-between items-center py-2.5">
<div class="flex flex-col gap-1">
<div class="text-sm font-medium text-gray-900">Tanggal Diperbarui</div>
<div class="text-gray-700 text-2sm">{{ dateFormat($bucok->updated_at, true) }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -8,7 +8,7 @@
{{ $title }}
</h3>
<div class="flex gap-2 items-center">
<div class="flex items-center gap-2">
{!! $customlink ?? "" !!}
@if (isset($id))
@php
@@ -31,65 +31,65 @@
@endif
</div>
</div>
<div class="grid grid-cols-3 card-body lg:py-7.5">
<div class="card-body lg:py-7.5 grid grid-cols-3">
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
<h3 class="text-md font-medium text-gray-900">
Nomor Register Permohonan:
</h3>
<span class="text-gray-700 text-2sm">
<span class="text-2sm text-gray-700">
{{ $permohonan->nomor_registrasi }}
</span>
</div>
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
<h3 class="text-md font-medium text-gray-900">
Pemohon:
</h3>
<span class="text-gray-700 text-2sm">
<span class="text-2sm text-gray-700">
{{ $permohonan->user->nik ?? '' }} | {{ $permohonan->user->name ?? '' }} | {{ $permohonan->user->branch->name ?? '' }}
</span>
</div>
@if(isset($penawaran))
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
<h3 class="text-md font-medium text-gray-900">
Nomor Penawaran:
</h3>
<span class="text-gray-700 text-2sm">
<span class="text-2sm text-gray-700">
{{ $penawaran->code }}
</span>
</div>
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
<h3 class="text-md font-medium text-gray-900">
Nomor Penawaran:
</h3>
<span class="text-gray-700 text-2sm">
<span class="text-2sm text-gray-700">
{{ $penawaran->tujuanPenilaianKjpp->name }}
</span>
</div>
@else
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
<h3 class="text-md font-medium text-gray-900">
Tujan Permohonan:
</h3>
<span class="text-gray-700 text-2sm">
<span class="text-2sm text-gray-700">
{{ $permohonan->tujuanPenilaian->name }}
</span>
</div>
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
<h3 class="text-md font-medium text-gray-900">
Nilai Plafond:
</h3>
<span class="text-gray-700 text-2sm">
<span class="text-2sm text-gray-700">
{{ $permohonan->nilaiPlafond->name ?? '' }}
</span>
</div>
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
<h3 class="text-md font-medium text-gray-900">
Status Bayar:
</h3>
<span
@@ -102,18 +102,18 @@
</div>
</div>
<div class="min-w-full border card border-agi-100">
<div class="card border border-agi-100 min-w-full">
<div class="card-header light:bg-agi-50">
<h3 class="card-title">
Detail Debitur
</h3>
</div>
<div class="pb-3 card-table scrollable-x-auto">
<div class="grid grid-cols-1 gap-5 xl:grid-cols-2 lg:gap-7.5">
<div class="card-table scrollable-x-auto pb-3">
<div class="grid grid-cols-1 xl:grid-cols-2 gap-5 lg:gap-7.5">
<div class="col-span-1">
<table class="table text-sm text-gray-500 align-middle">
<table class="table align-middle text-sm text-gray-500">
<tr>
<td class="py-2 font-normal text-gray-600">
<td class="py-2 text-gray-600 font-normal">
Nama
</td>
<td class="py-2 text-gray-800 font-normaltext-sm">
@@ -124,7 +124,7 @@
<td class="py-3">
Email
</td>
<td class="py-3 font-normal text-gray-700 text-2sm">
<td class="py-3 text-gray-700 text-2sm font-normal">
{{ $permohonan->debiture->email ?? '' }}
</td>
</tr>
@@ -132,24 +132,24 @@
<td class="py-3">
No Hp
</td>
<td class="py-3 font-normal text-gray-700 text-2sm">
<td class="py-3 text-gray-700 text-2sm font-normal">
{{ $permohonan->debiture->phone ?? '' }}
</td>
</tr>
<tr>
<td class="py-3 font-normal text-gray-600">
<td class="py-3 text-gray-600 font-normal">
Alamat
</td>
<td class="py-3 text-sm font-normal text-gray-700">
<td class="py-3 text-gray-700 text-sm font-normal">
{{ $permohonan->debiture->address ?? '' }}
</td>
</tr>
<tr>
<td class="py-3 font-normal text-gray-600">
<td class="py-3 text-gray-600 font-normal">
&nbsp;
</td>
<td class="py-3 text-sm font-normal text-gray-700">
<td class="py-3 text-gray-700 text-sm font-normal">
{{ $permohonan->debiture->village->name ?? '' }},
{{ $permohonan->debiture->district->name ?? '' }},
{{ $permohonan->debiture->city->name ?? '' }},
@@ -160,9 +160,9 @@
</table>
</div>
<div class="col-span-1">
<table class="table text-sm text-gray-500 align-middle">
<table class="table align-middle text-sm text-gray-500">
<tr>
<td class="py-3 font-normal text-gray-600">
<td class="py-3 text-gray-600 font-normal">
Cabang
</td>
<td class="py-2 text-gray-800 font-normaltext-sm">
@@ -170,7 +170,7 @@
</td>
</tr>
<tr>
<td class="py-3 font-normal text-gray-600">
<td class="py-3 text-gray-600 font-normal">
CIF
</td>
<td class="py-2 text-gray-800 font-normaltext-sm">
@@ -178,10 +178,10 @@
</td>
</tr>
<tr>
<td class="py-3 font-normal text-gray-600">
<td class="py-3 text-gray-600 font-normal">
Nomor Rekening
</td>
<td class="py-3 text-sm font-normal text-gray-700">
<td class="py-3 text-gray-700 text-sm font-normal">
{{ $permohonan->debiture->nomor_rekening ?? '' }}
</td>
</tr>
@@ -189,7 +189,7 @@
<td class="py-3">
NPWP
</td>
<td class="py-3 font-normal text-gray-700 text-2sm">
<td class="py-3 text-gray-700 text-2sm font-normal">
{{ $permohonan->debiture->npwp ?? '' }}
</td>
</tr>
@@ -201,172 +201,160 @@
<div class="min-w-full border card border-agi-100">
<div class="card border border-agi-100 min-w-full">
<div class="card-header light:bg-agi-50">
<h3 class="card-title">
Data Jaminan
</h3>
</div>
@endif
<div data-accordion="true">
@foreach ($permohonan->documents as $dokumen)
<div class="accordion-item {{ count($permohonan->documents) == 1 ? 'active' : '' }} [&:not(:last-child)]:border-b border-b-gray-200"
data-accordion-item="true" id="accordion_detail_jaminan">
<button @class(['accordion-toggle py-4 group', 'mx-8' => !isset($status)])"
data-accordion-toggle="#accordion_detail_jaminan_{{ $loop->index }}">
<span class="text-base font-medium text-gray-900">
@endif
<div data-accordion="true">
@foreach ($permohonan->documents as $dokumen)
<div class="accordion-item {{ count($permohonan->documents) == 1 ? 'active' : '' }} [&:not(:last-child)]:border-b border-b-gray-200"
data-accordion-item="true" id="accordion_detail_jaminan">
<button @class(['accordion-toggle py-4 group', 'mx-8' => !isset($status)])"
data-accordion-toggle="#accordion_detail_jaminan_{{ $loop->index }}">
<span class="text-base text-gray-900 font-medium">
@if (count($permohonan->documents) > 1)
Jaminan {{ $loop->index + 1 }}
@else
Jaminan
@endif
Jaminan {{ $loop->index + 1 }}
@else
Jaminan
@endif
</span>
<i class="block text-gray-600 ki-outline ki-plus text-2sm accordion-active:hidden">
</i>
<i class="hidden text-gray-600 ki-outline ki-minus text-2sm accordion-active:block">
</i>
</button>
<div class="accordion-content {{ count($permohonan->documents) > 1 ? 'hidden' : '' }}"
id="accordion_detail_jaminan_{{ $loop->index }}">
<i class="ki-outline ki-plus text-gray-600 text-2sm accordion-active:hidden block">
</i>
<i class="ki-outline ki-minus text-gray-600 text-2sm accordion-active:block hidden">
</i>
</button>
<div class="accordion-content {{ count($permohonan->documents) > 1 ? 'hidden' : '' }}"
id="accordion_detail_jaminan_{{ $loop->index }}">
@if (!isset($status))
<div class="grid grid-cols-2 card-body lg:py-7.5">
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
Pemilik Jaminan:
</h3>
<span class="text-gray-700 text-2sm">
@if (!isset($status))
<div class="card-body lg:py-7.5 grid grid-cols-2">
<div class="mb-5">
<h3 class="text-md font-medium text-gray-900">
Pemilik Jaminan:
</h3>
<span class="text-2sm text-gray-700">
{{ $dokumen->pemilik->name ?? '' }}
</span>
</div>
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
Jenis Jaminan:
</h3>
<span class="text-gray-700 text-2sm">
</div>
<div class="mb-5">
<h3 class="text-md font-medium text-gray-900">
Jenis Jaminan:
</h3>
<span class="text-2sm text-gray-700">
{{ $dokumen->jenisJaminan->name ?? '' }}
</span>
</div>
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
Hubungan Pemilik Jaminan:
</h3>
<span class="text-gray-700 text-2sm">
</div>
<div class="mb-5">
<h3 class="text-md font-medium text-gray-900">
Hubungan Pemilik Jaminan:
</h3>
<span class="text-2sm text-gray-700">
{{ $dokumen->pemilik->hubungan_pemilik->name ?? '' }}
</span>
</div>
<div class="mb-5">
<h3 class="font-medium text-gray-900 text-md">
Alamat Jaminan:
</h3>
<span class="text-gray-700 text-2sm">
{{ formatAlamat($dokumen) }}
</div>
<div class="mb-5">
<h3 class="text-md font-medium text-gray-900">
Alamat Jaminan:
</h3>
<span class="text-2sm text-gray-700">
{{ formatAlamat($dokumen) }}
</span>
</div>
</div>
@endif
<div class="card-table scrollable-x-auto pb-3">
<a href="{{ route('debitur.jaminan.bulk.download', ['id' => $permohonan->debiture->id, 'jaminan' => $dokumen->id]) }}"
class="ml-6 btn btn-dark dark:btn-light">
<i class="ki-outline ki-cloud-download"></i> Download Semua Dokumen
</a>
<table class="table align-middle text-sm text-gray-500">
@php $document = $dokumen; @endphp
@foreach($document->detail as $detail)
@if(isset($detail->dokumen_jaminan))
<tr>
<td colspan="2">
<strong class="text-2xs text-gray-600 uppercase">
{{ $loop->index+1 }}. {{ $detail->jenisLegalitasJaminan->name }}
</strong>
</td>
</tr>
@if(isset($detail->dokumen_jaminan))
@php
$dokumen_jaminan = is_array(json_decode($detail->dokumen_jaminan)) ? json_decode($detail->dokumen_jaminan) : [$detail->dokumen_jaminan];
$dokumen_nomor = is_array(json_decode($detail->dokumen_nomor)) ? json_decode($detail->dokumen_nomor) : ($detail->dokumen_nomor ? [$detail->dokumen_nomor] : []);
@endphp
@foreach($dokumen_jaminan as $index => $dokumen)
<tr>
<td>
<span class="text-2xs text-gray-600 uppercase pl-3">
{{ $loop->index+1 }}. Nomor : {{ $dokumen_nomor[$index] ?? '' }}
</span>
</td>
<td class="text-left">
@if(in_array(Auth::user()->roles[0]->name,['administrator','pemohon-eo']))
<a href="{{ route('debitur.jaminan.download', ['id' => $permohonan->debiture->id, 'dokumen' => $detail->id, 'index' => $index]) }}"
class="flex-none badge badge-sm badge-outline mt-2 mr-2">
{{ basename($dokumen) }}
<i class="ki-filled ki-cloud-download"></i>
</a>
@endif
<span class="badge badge-sm badge-outline badge-warning mt-2" onclick="viewPDF('{{ Storage::url($dokumen_jaminan[$index]) }}')">
<i class="ki-filled ki-eye mr-2"></i>Preview
</span>
</td>
</tr>
@if(isset($detail->details) && isset(json_decode($detail->details)[$index]))
@foreach (json_decode($detail->details)[$index] as $key => $value)
<tr>
<td>
<span class="text-2xs text-gray-600 uppercase pl-3">
- {{ str_replace("_"," ",$key) ?? "" }}
</span>
</td>
<td class="text-left">
<p class="text-2xs text-gray-600 max-w-[250px]">
{{ $value }}
</p>
</td>
</tr>
@endforeach
@endif
@endforeach
@endif
<tr>
<td>
<span class="text-2xs text-gray-600 uppercase pl-3">
- keterangan
</span>
</td>
<td class="text-right">
<p class="text-2xs text-gray-600 max-w-[250px]">
{{ $detail->keterangan }}
</p>
</td>
</tr>
@endif
@endforeach
</table>
</div>
</div>
@endif
<div class="pb-3 card-table scrollable-x-auto">
<a href="{{ route('debitur.jaminan.bulk.download', ['id' => $permohonan->debiture->id, 'jaminan' => $dokumen->id]) }}"
class="ml-6 btn btn-dark dark:btn-light">
<i class="ki-outline ki-cloud-download"></i> Download Semua Dokumen
</a>
<table class="table text-sm text-gray-500 align-middle">
@php $document = $dokumen; @endphp
@foreach ($document->detail as $detail)
@if (isset($detail->dokumen_jaminan))
<tr>
<td colspan="2">
<strong class="text-gray-600 uppercase text-2xs">
{{ $loop->index + 1 }}. {{ $detail->jenisLegalitasJaminan->name }}
</strong>
</td>
</tr>
@if (isset($detail->dokumen_jaminan))
@php
$dokumen_jaminan = is_array(json_decode($detail->dokumen_jaminan))
? json_decode($detail->dokumen_jaminan)
: [$detail->dokumen_jaminan];
$dokumen_nomor = is_array(json_decode($detail->dokumen_nomor))
? json_decode($detail->dokumen_nomor)
: ($detail->dokumen_nomor
? [$detail->dokumen_nomor]
: []);
@endphp
@foreach ($dokumen_jaminan as $index => $dokumen)
<tr>
<td>
<span class="pl-3 text-gray-600 uppercase text-2xs">
{{ $loop->index + 1 }}. Nomor : {{ $dokumen_nomor[$index] }}
</span>
</td>
<td class="text-left">
@if (in_array(Auth::user()->roles[0]->name, [
'administrator',
'pemohon-eo',
'surveyor',
'senior-officer',
'EO Appraisal',
'DD Appraisal',
]))
<a href="{{ route('debitur.jaminan.download', ['id' => $permohonan->debiture->id, 'dokumen' => $detail->id, 'index' => $index]) }}"
class="flex-none mt-2 mr-2 badge badge-sm badge-outline">
{{ basename($dokumen) }}
<i class="ki-filled ki-cloud-download"></i>
</a>
@endif
<span class="mt-2 badge badge-sm badge-outline badge-warning"
onclick="viewPDF('{{ Storage::url($dokumen_jaminan[$index]) }}')">
<i class="mr-2 ki-filled ki-eye"></i>Preview
</span>
</td>
</tr>
@if (isset($detail->details) && isset(json_decode($detail->details)[$index]))
@foreach (json_decode($detail->details)[$index] as $key => $value)
<tr>
<td>
<span class="pl-3 text-gray-600 uppercase text-2xs">
- {{ str_replace('_', ' ', $key) ?? '' }}
</span>
</td>
<td class="text-left">
<p class="text-2xs text-gray-600 max-w-[250px]">
{{ $value }}
</p>
</td>
</tr>
@endforeach
@endif
@endforeach
@endif
<tr>
<td>
<span class="pl-3 text-gray-600 uppercase text-2xs">
- keterangan
</span>
</td>
<td class="text-right">
<p class="text-2xs text-gray-600 max-w-[250px]">
{{ $detail->keterangan }}
</p>
</td>
</tr>
@endif
@endforeach
</table>
</div>
</div>
@endforeach
</div>
@endforeach
</div>
@if (!isset($status))
@if (!isset($status))
</div>
@include('lpj::component.lampiran-dokumen')
@include('lpj::component.history-permohonan')
@endif
@include('lpj::component.pdfviewer')

View File

@@ -1,4 +1,4 @@
<div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
@foreach ($permohonan->documents as $dokumen)
@if ($dokumen->jenisJaminan)
@@ -32,7 +32,7 @@
<div class="card">
<div class="card-header bg-agi-50">
<h3 class="uppercase card-title">
<h3 class="card-title uppercase">
Informasi dan pembanding
</h3>
<div class="card-tools">
@@ -47,14 +47,14 @@
<div class="card">
<div class="card-header bg-agi-50">
<h3 class="uppercase card-title">
<h3 class="card-title uppercase">
total nilai pasar wajar
</h3>
</div>
<div class="gap-5 card-body">
<div class="card-body gap-5">
<div>
<label for="total_nilai_pasar_wajar" class="form-label">Total Nilai Pasar Wajar</label>
<div class="grid gap-2.5 card-body">
<div class="card-body grid gap-2.5 ">
@php
$labelNilai = [
@@ -76,67 +76,72 @@
@foreach ($kategoriUnik as $item)
@php
$luas = match ($item) {
'bangunan' => $forminspeksi['bangunan']['luas_tanah_bagunan']['tidak sesuai'] ??
$forminspeksi['bangunan']['luas_tanah_bagunan']['sesuai'] ?? null,
'tanah' => $forminspeksi['tanah']['luas_tanah']['tidak sesuai'] ??
$forminspeksi['tanah']['luas_tanah']['sesuai'] ?? null,
'apartemen-kantor' => $forminspeksi['luas_unit']['sesuai'] ??
$forminspeksi['luas_unit']['tidak sesuai'] ?? null,
default => null,
};
if ($item === 'bangunan') {
$luas =
$forminspeksi['bangunan']['luas_tanah_bagunan']['tidak sesuai'] ??
($forminspeksi['bangunan']['luas_tanah_bagunan']['sesuai'] ?? null);
} elseif ($item === 'tanah') {
$luas =
$forminspeksi['tanah']['luas_tanah']['tidak sesuai'] ??
($forminspeksi['tanah']['luas_tanah']['sesuai'] ?? null);
} else if ($item === 'apartemen-kantor') {
$luas = $forminspeksi['luas_unit']['sesuai'] ?? ($forminspeksi['luas_unit']['tidak sesuai'] ?? null);
} else {
$luas = null;
}
$luas = old('luas_' . $item, $lpjData['luas_' . $item] ?? $luas);
$luasKey = 'luas_' . $item;
$nilaiKey1 = 'nilai_' . $item . '_1';
$nilaiKey2 = 'nilai_' . $item . '_2';
@endphp
@if (isset($labelNilai[$item]))
<div class="flex gap-2.5 w-full grid-col-3">
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<label for="province" class="form-label max-w-56">
{{ $labelNilai[$item] }}
</label>
<input type="text" id="{{ $luasKey }}" class="w-full input"
name="{{ $luasKey }}"
value="{{ old($luasKey, $lpjData[$luasKey] ?? null) }}"
oninput="calculateTotal()">
</div>
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<label for="province" class="">X</label>
<label class="input">
<i class="">Rp</i>
<input type="text" id="{{ $nilaiKey1 }}" class="w-full currency"
name="{{ $nilaiKey1 }}"
value="{{ old($nilaiKey1, $lpjData[$nilaiKey1] ?? null) }}"
oninput="calculateTotal()">
</label>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<label class="input">
<i class="">Rp</i>
<input id="{{ $nilaiKey2 }}" type="text"
class="w-full currency-format"
name="{{ $nilaiKey2 }}"
value="{{ old($nilaiKey2, $lpjData[$nilaiKey2] ?? null) }}">
</label>
</div>
</div>
@endif
@if (isset($labelNilai[$item]))
<div class="flex grid-col-3 gap-2.5 w-full">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<label for="province" class="form-label max-w-56">
{{ $labelNilai[$item] }}
</label>
<input type="text" id="{{ $luasKey }}" class="input w-full"
name="{{ $luasKey }}"
value="{{ old($luasKey, $lpjData[$luasKey] ?? null) }}"
oninput="calculateTotal()">
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<label for="province" class="">X</label>
<label class="input">
<i class="">Rp</i>
<input type="text" id="{{ $nilaiKey1 }}" class="w-full currency"
name="{{ $nilaiKey1 }}"
value="{{ old($nilaiKey1, $lpjData[$nilaiKey1] ?? null) }}"
oninput="calculateTotal()">
</label>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<label class="input">
<i class="">Rp</i>
<input id="{{ $nilaiKey2 }}" type="text"
class="w-full currency-format"
name="{{ $nilaiKey2 }}"
value="{{ old($nilaiKey2, $lpjData[$nilaiKey2] ?? null) }}">
</label>
</div>
</div>
@endif
@endforeach
<div>
<div id="add_pasar_wajar" class="flex flex-wrap gap-2.5 w-full">
<div id="add_pasar_wajar" class="flex flex-wrap gap-2.5 w-full">
</div>
</div>
<div class="flex gap-2 justify-end">
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap grid-col-2">
<div class="flex justify-end gap-2 ">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full grid-col-2">
<label class="form-label max-w-56" for="">Total Nilai</label>
<label for="province" class="input">
<i class="">Rp
</i>
<input type="text" class="w-full currency-format" id="total_nilai_pasar_wajar"
<input type="text" class=" w-full currency-format" id="total_nilai_pasar_wajar"
name="total_nilai_pasar_wajar"
value="{{ old('total_nilai_pasar_wajar', $lpjData['total_nilai_pasar_wajar'] ?? null) }}">
</label>
@@ -146,22 +151,22 @@
</div>
<div class="mb-10 tambah" style="margin-bottom: 20px;">
<div class="tambah mb-10" style="margin-bottom: 20px;">
<button type="button" id="tambah-npw" class="btn btn-primary">
<i class="ki-filled ki-plus"></i>
Tambah NPW </button>
</div>
<div>
<label for="total_nilai_pasar_wajar" class="uppercase form-label">TOTAL NILAI
<label for="total_nilai_pasar_wajar" class="form-label uppercase">TOTAL NILAI
LIKUIDASI</label>
<div class="grid gap-2.5 w-full card-body">
<div class="flex gap-2.5 w-full grid-col-3">
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<div class="card-body grid gap-2.5 w-full">
<div class="flex grid-col-3 gap-2.5 w-full">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<div class="w-full">
<label class="input">
<input type="text" id="likuidasi" name="likuidasi" class="w-full currency"
<input type="text" id="likuidasi" name="likuidasi" class=" w-full currency"
value="{{ old('likuidasi', $lpjData['likuidasi'] ?? null) }}"
oninput="calculateTotal()">
<i class="">%
@@ -171,24 +176,24 @@
</span>
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<label for="province" class="">X</label>
<label class="input">
<i class="">Rp
</i>
<input type="text" class="w-full currency-format" id="likuidasi_nilai_1"
<input type="text" class=" w-full currency-format" id="likuidasi_nilai_1"
name="likuidasi_nilai_1"
value="{{ old('likuidasi_nilai_1', $lpjData['likuidasi_nilai_1'] ?? null) }}"
oninput="calculateTotal()">
</label>
</div>
<div class="grid gap-2.5 w-full">
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<label class="input">
<i class="">Rp
</i>
<input type="text" class="w-full currency-format" name="likuidasi_nilai_2"
<input type="text" class=" w-full currency-format" name="likuidasi_nilai_2"
value="{{ old('likuidasi_nilai_2', $lpjData['likuidasi_nilai_2'] ?? null) }}">
</div>
@@ -198,16 +203,16 @@
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap" style="margin-top: 20px">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5" style="margin-top: 20px">
<label class="form-label lg:form-label max-w-56">Catatan yang Perlu Diperhatikan
<label class="form-label lg:form-label max-w-56 ">Catatan yang Perlu Diperhatikan
</label>
<div class="w-full">
<div id="keterangan_penilai-container" class="flex flex-wrap gap-2.5 items-baseline w-full">
<div id="keterangan_penilai-container" class="flex items-baseline flex-wrap gap-2.5 w-full">
@if (!empty($lpjData['keterangan_penilai']) && is_array($lpjData['keterangan_penilai']))
@foreach ($lpjData['keterangan_penilai'] as $index => $item)
<div class="flex gap-2 items-center mt-2 w-full keterangan_penilai textarea-group">
<textarea name="keterangan_penilai[]" class="mt-2 textarea" placeholder="Masukkan catatan penting" rows="10">{{ old("keterangan_penilai.$index", $item) }}</textarea>
<div class="keterangan_penilai flex items-center gap-2 mt-2 textarea-group w-full">
<textarea name="keterangan_penilai[]" class="textarea mt-2 " placeholder="Masukkan catatan penting" rows="10">{{ old("keterangan_penilai.$index", $item) }}</textarea>
<button class="btn btn-danger btn-sm remove-btn" type="button"
style="display: none;">
<i class="ki-outline ki-trash"></i>
@@ -215,19 +220,19 @@
</div>
@endforeach
@else
<div class="flex gap-2 items-center mt-2 w-full keterangan_penilai textarea-group">
<textarea name="keterangan_penilai[]" class="mt-2 textarea" placeholder="Masukkan catatan penting" rows="10"></textarea>
<div class="keterangan_penilai flex items-center gap-2 mt-2 textarea-group w-full">
<textarea name="keterangan_penilai[]" class="textarea mt-2 " placeholder="Masukkan catatan penting" rows="10"></textarea>
<button class="btn btn-danger btn-sm remove-btn" type="button"
style="display: none;">
<i class="ki-outline ki-trash"></i>
</button>
<em id="error-keterangan_penilai" class="text-sm alert text-danger"></em>
<em id="error-keterangan_penilai" class="alert text-danger text-sm"></em>
</div>
@endif
</div>
<button type="button"
onclick="addClonableItem('keterangan_penilai-container', 'keterangan_penilai')"
class="mt-5 btn btn-primary btn-sm">
class="btn btn-primary btn-sm mt-5 ">
<i class="ki-outline ki-plus"></i>
</button>
</div>
@@ -251,40 +256,40 @@
const newNPWRow = document.createElement('div');
newNPWRow.className = 'flex grid-col-3 gap-2.5 w-full npw-row';
newNPWRow.innerHTML = `
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<input type="text"
id="name_npw_${npwCounter}"
class="w-full input"
class="input w-full"
name="name_npw_${npwCounter}"
placeholder="Nama NPW">
<input type="text"
id="ls_npw_${npwCounter}"
class="w-full input"
class="input w-full "
name="luas_npw_${npwCounter}"
placeholder="Luas NPW"
oninput="calculateTotal()">
</div>
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<label for="province" class="">X</label>
<label class="input">
<i class="">Rp
</i>
<input type="text"
id="nilai_npw_${npwCounter}_1"
class="w-full currency"
class=" w-full currency"
name="nilai_npw_${npwCounter}_1"
placeholder="Harga per meter"
oninput="calculateTotal()">
</label>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<label class="input">
<i class="">Rp
</i>
<input type="text"
id="nilai_npw_${npwCounter}_2"
class="w-full input currency-format"
class="input w-full currency-format"
name="nilai_npw_${npwCounter}_2"
placeholder="Total Nilai"
readonly>
@@ -328,16 +333,16 @@
newNPWRow.className = 'flex grid-col-3 gap-2.5 w-full npw-row mb-3';
newNPWRow.innerHTML = `
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<input type="text"
id="name_npw_${npwCounter}"
class="w-full input"
class="input w-full"
name="name_npw_${npwCounter}"
placeholder="Nama NPW"
value="${npw.name || ''}">
<input type="text"
id="ls_npw_${npwCounter}"
class="w-full input currency-format"
class="input w-full currency-format"
name="luas_npw_${npwCounter}"
placeholder="Luas NPW"
value="${npw.luas || ''}"
@@ -345,34 +350,34 @@
</div>
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<label for="province" class="">X</label>
<label class="input">
<i class="">Rp
</i>
<input type="text"
id="nilai_npw_${npwCounter}_1"
class="w-full currency"
class=" w-full currency"
name="nilai_npw_${npwCounter}_1"
placeholder="Harga per meter"
value="${npw.nilai_1 || ''}"
oninput="calculateTotal()">
</label>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline w-full lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 w-full">
<label class="input">
<i class="">Rp
</i>
<input type="text"
id="nilai_npw_${npwCounter}_2"
class="w-full currency-format"
class=" w-full currency-format"
name="nilai_npw_${npwCounter}_2"
placeholder="Total Nilai"
value="${npw.nilai_2 || ''}"
readonly>
</label>
<div>
<button type="button" class="h-full btn btn-danger remove-npw">
<button type="button" class="btn btn-danger remove-npw h-full">
<i class="ki-filled ki-minus"></i>
</button>
</div>
@@ -438,18 +443,13 @@
function calculateTotal() {
const parseInput = (value) => {
if (!value) return 0;
// keep the dot as decimal separator, remove thousand separators
const cleaned = value.replace(/[^0-9.,]/g, '').replace(/\./g, '').replace(/,/g, '.');
return parseFloat(cleaned) || 0;
return parseFloat(value.replace(/[^0-9]/g, '')) || 0;
};
// Function to format currency
function formatCurrency(value) {
// Ensure we have a valid number to format
//const num = parseFloat(value.replace(/[^0-9]/g, '')) || 0;
//return num.toLocaleString('id-ID');
let num = value.replace(/[^0-9.,]/g, '').replace(/\./g, '').replace(/,/g, '.');
num = parseFloat(num) || 0;
const num = parseFloat(value.replace(/[^0-9]/g, '')) || 0;
return num.toLocaleString('id-ID');
}
@@ -469,13 +469,15 @@
kategoriItems.forEach(item => {
console.log(item);
const kategori = item.id.replace('luas_', '');
const luasInput = document.getElementById(`luas_${kategori}`);
const nilaiInput = document.querySelector(`input[name="nilai_${kategori}_1"]`);
const outputElement = document.querySelector(`input[name="nilai_${kategori}_2"]`);
if (luasInput && nilaiInput && outputElement) {
const luas = parseFloat(luasInput.value.replace(/[^0-9.]/g, '')) || 0;
const luas = parseInput(luasInput.value);
const nilai = parseInput(nilaiInput.value);
const hasil = luas * nilai;

View File

@@ -37,7 +37,7 @@
@endforelse
</div>
@if(Auth::user()->hasRole(['Penilai', 'administrator','penilai','admin','surveyor','pemohon-ao','pemohon-eo']))
@if(Auth::user()->hasRole(['Penilai', 'administrator','penilai','admin','surveyor']))
<form action="{{ route('lampiran.upload') }}" method="POST" enctype="multipart/form-data" class="mt-6">
@csrf
<input type="hidden" name="permohonan_id" value="{{ $permohonan->id }}">
@@ -61,7 +61,7 @@
</div>
<div class="md:col-span-2">
<label for="keterangan" class="block text-sm font-medium text-gray-700">Keterangan</label>
<textarea name="keterangan" rows="3" class="textarea mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"></textarea>
<textarea name="keterangan" id="keterangan" rows="3" class="textarea mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"></textarea>
</div>
</div>
<div class="mt-4">

View File

@@ -1,5 +1,10 @@
@if (isset($dokumen))
@if (@isset($dokumen))
@foreach ($dokumen->detail as $detail)
{{-- @if (!empty($detail->name) && isset($detail->details) && !empty($detail->dokumen_jaminan))
<tr>
<td width="25%"><strong>{{ $detail->name ?? '' }}</strong></td>
</tr>
@endif --}}
@if ($detail->name != 'LOKASI JAMINAN')
@if (isset($detail->details))
@php

View File

@@ -1,11 +1,11 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render(request()->route()->getName()) }}
{{-- {{ Breadcrumbs::render(request()->route()->getName()) }} --}}
@endsection
@section('content')
<div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<form
action="{{ isset($daftarPustaka->id) ? route('daftar-pustaka.update', $daftarPustaka->id) : route('daftar-pustaka.store') }}"
method="POST" enctype="multipart/form-data">
@@ -15,18 +15,18 @@
@method('PUT')
@endif
<div class="pb-2.5 border card border-agi-100">
<div class="card border border-agi-100 pb-2.5">
<div class="card-header bg-agi-50" id="basic_settings">
<h3 class="card-title">
{{ isset($daftarPustaka->id) ? 'Edit' : 'Tambah' }} Daftar Pustaka
</h3>
<div class="flex gap-2 items-center">
<div class="flex items-center gap-2">
<a href="{{ route('daftar-pustaka.index') }}" class="btn btn-xs btn-info"><i
class="ki-filled ki-exit-left"></i> Back</a>
</div>
</div>
<div class="grid gap-5 card-body">
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<div class="card-body grid gap-5">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Judul
</label>
@@ -34,11 +34,11 @@
<input class="input @error('judul') border-danger bg-danger-light @enderror" type="text"
name="judul" value="{{ $daftarPustaka->judul ?? old('judul') }}">
@error('judul')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Upload File
</label>
@@ -46,32 +46,32 @@
<input class="input @error('attachment') border-danger bg-danger-light @enderror" type="file"
name="attachment">
@error('attachment')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
@if (isset($daftarPustaka->attachment))
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
File
</label>
<div class="flex flex-wrap items-baseline w-full">
{{-- ambil nama file pathnya hilangkan --}}
<a href="{{ asset($daftarPustaka->attachment) }}" class="badge badge-outline badge-md badge-info">{{ basename($daftarPustaka->attachment) }} &nbsp;
<a href="{{ asset('storage/' .$daftarPustaka->attachment) }}" class="badge badge-outline badge-md badge-info">{{ basename($daftarPustaka->attachment) }} &nbsp;
<i class="ki-filled ki-cloud-download"></i>
</a>
</div>
</div>
@endif
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Kategori
</label>
<div class="flex flex-wrap items-baseline w-full">
<select class="w-full select tomselect" name="category_id">
<select class="select tomselect w-full" name="category_id">
<option value="">Pilih Kategori</option>
@if (isset($categories))
@foreach ($categories as $item)
@@ -83,19 +83,19 @@
@endif
</select>
@error('category_id')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Deskripsi
</label>
<div class="flex flex-wrap items-baseline w-full">
<textarea name="deskripsi" class="textarea" id="" cols="30" rows="10">{{ $daftarPustaka->deskripsi ?? old('deskripsi') }}</textarea>
@error('deskripsi')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>

View File

@@ -1,23 +1,23 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render(request()->route()->getName()) }}
{{-- {{ Breadcrumbs::render('basicdata.ijin_usaha') }} --}}
@endsection
@section('content')
<style>
@media (max-width: 768px) {
#previewContent {
height: 400px;
overflow: auto;
}
#previewContent {
height: 400px;
overflow: auto;
}
}
</style>
<div class="flex flex-col gap-7 items-stretch">
<div class="flex gap-3 items-center w-full">
<div class="w-full input">
<div class="flex flex-col items-stretch gap-7">
<div class="flex items-center gap-3 w-full">
<div class="input w-full">
<i class="ki-filled ki-magnifier">
</i>
<input id="search" placeholder="Search Daftar Pustaka, Judul" type="text">
@@ -37,8 +37,8 @@
</a>
</div>
<div class="flex flex-wrap gap-5 justify-between items-center mt-3">
<h3 class="text-sm font-medium text-mono">
<div class="flex flex-wrap items-center gap-5 justify-between mt-3">
<h3 class="text-sm text-mono font-medium">
page {{ $page }} of {{ $pageCount }} {{ $limit }} items per page, total
{{ $total }} items.
</h3>
@@ -71,15 +71,15 @@
<div class="grid grid-cols-1 gap-4 md:grid-cols-3 lg:grid-cols-4" id="daftar_pustaka_grid">
<div class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4 " id="daftar_pustaka_grid">
@if (isset($daftar_pustaka))
@foreach ($daftar_pustaka as $item)
<div class="border shadow-none card">
<div class="card border shadow-none ">
<a class="show-pustaka h-[300px] bg-gray-200 w-full block" href="{{ route('daftar-pustaka.show', $item->id) }}"
data-url="{{ $item->attachment }}">
<div class="flex overflow-hidden justify-center items-center p-4 w-full h-full">
<div class="flex justify-center items-center text-red-500 rounded">
<i class="text-3xl ki-filled ki-document"></i>
<div class="p-4 h-full w-full flex items-center justify-center overflow-hidden">
<div class=" text-red-500 flex items-center justify-center rounded">
<i class="ki-filled ki-document text-3xl"></i>
</div>
</div>
</a>
@@ -87,15 +87,15 @@
<div class="card-body">
<a href="{{ route('daftar-pustaka.show', $item->id) }}">
<h3 class="font-medium text-gray-900 cursor-pointer text-md hover:text-primary">
<h3 class="text-md font-medium text-gray-900 hover:text-primary cursor-pointer">
{{ $item->judul }}</h3>
<p class="text-gray-700 text-2sm">
<p class="text-2sm text-gray-700">
{{-- batasi panjang deskripsi 50 --}}
{{ substr($item->deskripsi, 0, 50) }}
</p>
</a>
<div class="flex gap-2.5 justify-between items-center mt-2">
<p class="text-xs text-gray-700 rounded-full badge badge-xs badge-outline badge-success">
<div class="flex justify-between items-center gap-2.5 mt-2">
<p class="badge rounded-full badge-xs badge-outline badge-success text-xs text-gray-700">
# {{ $item->category->name }}</p>
@auth
@@ -124,20 +124,20 @@
</div>
<div class="grid hidden grid-cols-1 gap-4" id="daftar_pustaka_list">
<div class="grid grid-cols-1 gap-4 hidden" id="daftar_pustaka_list">
@if (isset($daftar_pustaka))
@foreach ($daftar_pustaka as $item)
<div class="card">
<div class="flex flex-wrap justify-between items-center p-2 card-body pe-5 gap-4.5">
<div class="flex gap-3.5 items-center">
<div class="card-body flex items-center flex-wrap justify-between p-2 pe-5 gap-4.5">
<div class="flex items-center gap-3.5">
<div
class="card bg-gray-200 flex items-center justify-center bg-accent/50 h-[70px] w-[90px] shadow-none">
<a class="show-pustaka h-[90px] w-full block"
href="{{ route('daftar-pustaka.show', $item->id) }}"
data-url="{{ $item->attachment }}">
<div class="flex overflow-hidden justify-center items-center p-4 w-full h-full">
<div class="flex justify-center items-center text-red-500 rounded">
<i class="text-3xl ki-filled ki-document"></i>
<div class="p-4 h-full w-full flex items-center justify-center overflow-hidden">
<div class=" text-red-500 flex items-center justify-center rounded">
<i class="ki-filled ki-document text-3xl"></i>
</div>
</div>
</a>
@@ -145,12 +145,12 @@
<a href="{{ route('daftar-pustaka.show', $item->id) }}">
<div class="flex flex-col gap-2 cursor-pointer">
<div class="flex items-center mt-1">
<a class="text-sm font-medium hover:text-primary text-mono leading-5.5">
<a class="hover:text-primary text-sm font-medium text-mono leading-5.5">
{{ $item->judul }}
</div>
</a>
<div class="flex flex-wrap gap-3 items-center">
<span class="gap-1 rounded-full kt-badge kt-badge-warning kt-badge-sm">
<div class="flex items-center flex-wrap gap-3">
<span class="kt-badge kt-badge-warning kt-badge-sm rounded-full gap-1">
<span class="text-xs font-medium text-foreground">
{{ substr($item->deskripsi, 0, 50) }}
</span>
@@ -160,8 +160,8 @@
</div>
</a>
</div>
<div class="flex gap-1.5 items-center">
<p class="text-xs text-gray-700 rounded-full badge badge-sm badge-outline badge-success">
<div class="flex items-center gap-1.5">
<p class="badge rounded-full badge-sm badge-outline badge-success text-xs text-gray-700">
# {{ $item->category->name }}</p>
@auth
@if (auth()->user()->hasRole(['administrator', 'admin']))
@@ -188,7 +188,7 @@
</div>
<div class="flex gap-2 justify-center mt-5 pagination">
<div class="pagination flex gap-2 justify-center mt-5">
@if ($daftar_pustaka->onFirstPage())
<span class="btn disabled"><i class="ki-filled ki-black-left"></i></span>
@else

View File

@@ -1,29 +1,28 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render(request()->route()->getName(), $daftarPustaka) }}
@endsection
@section('content')
<div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<form
action="{{ isset($daftarPustaka->id) ? route('daftar-pustaka.update', $daftarPustaka->id) : route('daftar-pustaka.store') }}"
method="POST">
@csrf
<div class="pb-2.5 border card border-agi-100">
<div class="card border border-agi-100 pb-2.5">
<div class="card-header bg-agi-50" id="basic_settings">
<h3 class="card-title">
{{ $daftarPustaka->judul ?? '' }}
</h3>
<div class="flex gap-2 items-center">
<div class="flex items-center gap-2">
<a href="{{ route('daftar-pustaka.index') }}" class="btn btn-xs btn-info">
<i class="ki-filled ki-exit-left"></i> Back
</a>
</div>
</div>
<div class="grid gap-5 card-body">
<div class="card-body grid gap-5">
<div class="min-w-3xl">
<div class="flex flex-col p-4 h-full">
<div class="p-4 h-full flex flex-col">
<div class="flex justify-between items-center mb-4">
<a href="{{ asset('storage/' . $daftarPustaka->attachment) }}" class="btn btn-primary btn-sm">
<i class="ki-duotone ki-cloud-download me-1"></i>
@@ -40,16 +39,16 @@
@endphp
@if ($isPdf)
<canvas id="pdfViewer" class="w-full rounded border touch-pan-y"></canvas>
<div class="flex gap-3 justify-center mt-3">
<canvas id="pdfViewer" class="w-full border rounded touch-pan-y"></canvas>
<div class="flex justify-center gap-3 mt-3">
<button type="button" id="prevPage" class="btn btn-primary btn-sm">Previous</button>
<span>Page: <span id="pageNum">1</span> / <span id="pageCount">0</span></span>
<button type="button" id="nextPage" class="btn btn-primary btn-sm">Next</button>
</div>
@elseif ($isImage)
<img src="{{ $fileUrl }}" class="object-contain w-full rounded" />
<img src="{{ $fileUrl }}" class="w-full object-contain rounded" />
@else
<p class="text-red-500">File tidak bisa ditampilkan, silakan
<p class="text-red-500">File tidak bisa ditampilkan, silakan
<a href="{{ $fileUrl }}" class="text-blue-500 underline" download>unduh di sini</a>.
</p>
@endif

View File

@@ -1,142 +1,139 @@
@extends('layouts.main')
@section('content')
@if (!Auth::user()->hasRole(['penilai', 'surveyor', 'pemohon-ao', 'pemohon-eo']))
<div class="grid gap-6 mx-auto w-full">
<!-- Header Section -->
<div class="flex flex-wrap gap-5 justify-between items-center pb-8 lg:items-end">
<div class="flex flex-col gap-3 justify-center">
<h1 class="text-2xl font-bold leading-none text-gray-800">
Dashboard
</h1>
<div class="flex gap-2 items-center text-sm font-medium text-gray-600">
<i class="ki-filled ki-clipboard"></i>
Selamat datang, <span class="font-semibold text-primary">{{ auth()->user()->name }}</span>
</div>
</div>
<div class="flex items-center gap-3 w-[30%]">
<div class="flex gap-2 items-center w-full">
<input type="date" class="input" name="start_date" id="start_date">
<input type="date" class="input" name="end_date" id="end_date">
</div>
<button class="btn btn-primary" id="filter" type="button" onclick="filterDashboard()">
<i class="ki-outline ki-filter fs-2 me-1"></i>
Filter</button>
<div class="w-full grid gap-6 mx-auto">
<!-- Header Section -->
<div class="flex flex-wrap items-center lg:items-end justify-between gap-5 pb-8">
<div class="flex flex-col justify-center gap-3">
<h1 class="text-2xl font-bold leading-none text-gray-800">
Dashboard
</h1>
<div class="flex items-center gap-2 text-sm font-medium text-gray-600">
<i class="ki-filled ki-clipboard"></i>
Selamat datang, <span class="text-primary font-semibold">{{ auth()->user()->name }}</span>
</div>
</div>
<div class="flex items-center gap-3 w-[30%]">
<div class="flex items-center gap-2 w-full">
<input type="date" class="input" name="start_date" id="start_date">
<input type="date" class="input" name="end_date" id="end_date">
</div>
<button class="btn btn-primary" id="filter" type="button" onclick="filterDashboard()">
<i class="ki-outline ki-filter fs-2 me-1"></i>
Filter</button>
</div>
</div>
<!-- Stats Cards -->
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
@foreach ($dashboard['count_resume'] as $status => $count)
@php
@foreach ($dashboard['count_resume'] as $status => $count)
@php
$gradientFrom = $status === 'batal' ? 'from-red-50' : 'from-amber-50';
$gradientTo = $status === 'batal' ? 'to-red-100' : 'to-amber-100';
$borderColor = $status === 'batal' ? 'border-red-200' : 'border-amber-200';
$iconBg = $status === 'batal' ? 'bg-red-500' : 'bg-amber-500';
$iconColor = $status === 'batal' ? 'text-red-500' : 'text-amber-500';
$textColor = $status === 'batal' ? 'text-red-800' : 'text-amber-800';
$cardTitle = $status === 'batal' ? 'Batal dari Cabang' : 'Menunggu Keputusan Cabang';
@endphp
$gradientFrom = $status === 'batal' ? 'from-red-50' : 'from-amber-50';
$gradientTo = $status === 'batal' ? 'to-red-100' : 'to-amber-100';
$borderColor = $status === 'batal' ? 'border-red-200' : 'border-amber-200';
$iconBg = $status === 'batal' ? 'bg-red-500' : 'bg-amber-500';
$iconColor = $status === 'batal' ? 'text-red-500' : 'text-amber-500';
$textColor = $status === 'batal' ? 'text-red-800' : 'text-amber-800';
$cardTitle = $status === 'batal' ? 'Batal dari Cabang' : 'Menunggu Keputusan Cabang';
@endphp
<div class="card bg-gradient-to-br {{ $gradientFrom }} {{ $gradientTo }} {{ $borderColor }}">
<div class="card-header border-b {{ $borderColor }}">
<div class="flex gap-3 items-center">
<div class="w-10 h-10 {{ $iconBg }} rounded-lg flex items-center justify-center">
<i
class="ki-filled {{ $status === 'batal' ? 'ki-cross-circle text-white' : 'ki-time text-white' }}"></i>
</div>
<h3 class="card-title {{ $textColor }} font-semibold">{{ $cardTitle }}</h3>
<div class="card bg-gradient-to-br {{ $gradientFrom }} {{ $gradientTo }} {{ $borderColor }}">
<div class="card-header border-b {{ $borderColor }}">
<div class="flex items-center gap-3">
<div class="w-10 h-10 {{ $iconBg }} rounded-lg flex items-center justify-center">
<i
class="ki-filled {{ $status === 'batal' ? 'ki-cross-circle text-white' : 'ki-time text-white' }}"></i>
</div>
<h3 class="card-title {{ $textColor }} font-semibold">{{ $cardTitle }}</h3>
</div>
<div class="card-body">
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<div
class="bg-white rounded-xl p-4 shadow-sm border {{ $borderColor }} hover:shadow-md transition-shadow">
<div class="flex justify-between items-center">
<div>
<h4 class="mb-1 text-sm font-semibold text-gray-700">Jabodetabek</h4>
<span
class="text-2xl font-bold {{ $status === 'batal' ? 'text-red-600' : 'text-amber-600' }}">
{{ $count['jabodetabek']['count_report'] ?? 0 }}
</span>
</div>
<div
class="w-8 h-8 {{ $gradientFrom }} rounded-full flex items-center justify-center">
<i class="ki-filled {{ $iconColor }}"></i>
</div>
</div>
<div class="card-body">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div
class="bg-white rounded-xl p-4 shadow-sm border {{ $borderColor }} hover:shadow-md transition-shadow">
<div class="flex items-center justify-between">
<div>
<h4 class="text-sm font-semibold text-gray-700 mb-1">Jabodetabek</h4>
<span
class="text-2xl font-bold {{ $status === 'batal' ? 'text-red-600' : 'text-amber-600' }}">
{{ $count['jabodetabek']['count_report'] ?? 0 }}
</span>
</div>
<div class="w-8 h-8 {{ $gradientFrom }} rounded-full flex items-center justify-center">
<i class="ki-filled {{ $iconColor }}"></i>
</div>
</div>
<div
class="bg-white rounded-xl p-4 shadow-sm border {{ $borderColor }} hover:shadow-md transition-shadow">
<div class="flex justify-between items-center">
<div>
<h4 class="mb-1 text-sm font-semibold text-gray-700">Luar Jabodetabek</h4>
<span
class="text-2xl font-bold {{ $status === 'batal' ? 'text-red-600' : 'text-amber-600' }}">
{{ $count['non-jabodetabek']['count_report'] ?? 0 }}
</span>
</div>
<div
class="w-8 h-8 {{ $gradientFrom }} rounded-full flex items-center justify-center">
<i class="ki-filled {{ $iconColor }}"></i>
</div>
</div>
<div
class="bg-white rounded-xl p-4 shadow-sm border {{ $borderColor }} hover:shadow-md transition-shadow">
<div class="flex items-center justify-between">
<div>
<h4 class="text-sm font-semibold text-gray-700 mb-1">Luar Jabodetabek</h4>
<span
class="text-2xl font-bold {{ $status === 'batal' ? 'text-red-600' : 'text-amber-600' }}">
{{ $count['non-jabodetabek']['count_report'] ?? 0 }}
</span>
</div>
<div class="w-8 h-8 {{ $gradientFrom }} rounded-full flex items-center justify-center">
<i class="ki-filled {{ $iconColor }}"></i>
</div>
</div>
</div>
</div>
</div>
@endforeach
</div>
<!-- Chart Section -->
<div class="card">
<div class="border-b border-blue-200 card-header">
<div class="flex justify-between items-center">
<div class="flex gap-3 items-center">
<div class="flex justify-center items-center w-10 h-10 bg-blue-500 rounded-lg">
<i class="text-white ki-filled ki-chart-simple-2"></i>
</div>
<h3 class="text-lg font-semibold text-blue-800">Pendapatan Appraisal</h3>
</div>
</div>
</div>
<div class="card-body">
<div class="p-4 bg-white rounded-xl shadow-sm">
<table class="table w-full table-auto">
@endforeach
</div>
<!-- Chart Section -->
<div class="card ">
<div class="card-header border-b border-blue-200">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-blue-500 rounded-lg flex items-center justify-center">
<i class="ki-filled ki-chart-simple-2 text-white"></i>
</div>
<h3 class="text-lg font-semibold text-blue-800">Pendapatan Appraisal</h3>
</div>
</div>
</div>
<div class="card-body">
<div class="bg-white rounded-xl p-4 shadow-sm">
<table class="table table-auto w-full">
<thead class="bg-gray-50">
<tr>
<th
class="px-4 py-3 text-xs font-semibold tracking-wider text-left text-gray-700 uppercase border-r border-gray-200">
class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider border-r border-gray-200">
Bulan
</th>
<th
class="px-4 py-3 text-xs font-semibold tracking-wider text-center text-gray-700 uppercase border-r border-gray-200">
class="px-4 py-3 text-center text-xs font-semibold text-gray-700 uppercase tracking-wider border-r border-gray-200">
Jumlah
</th>
<th
class="px-4 py-3 text-xs font-semibold tracking-wider text-center text-gray-700 uppercase">
class="px-4 py-3 text-center text-xs font-semibold text-gray-700 uppercase tracking-wider">
Akumulasi
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach ($dashboard['count_pendapatan'] as $month => $data)
<tr class="transition-colors hover:bg-gray-50">
<tr class="hover:bg-gray-50 transition-colors">
<!-- Kolom Nama month -->
<td class="px-4 py-3 text-sm font-medium text-gray-900 border-r border-gray-200">
{{ ucfirst($month) }}
</td>
<!-- Kolom Total Laporan -->
<td class="px-4 py-3 text-sm text-center text-gray-700 border-r border-gray-200">
<td class="px-4 py-3 text-sm text-gray-700 text-center border-r border-gray-200">
{{ $data['total_jumlah'] ?? '-' }}
</td>
<!-- Kolom Total Debitur -->
<td class="px-4 py-3 text-sm text-center text-gray-700 border-r border-gray-200">
<td class="px-4 py-3 text-sm text-gray-700 text-center border-r border-gray-200">
{{ $data['total_akumulasi'] ?? '-' }}
</td>
</tr>
@@ -144,156 +141,153 @@
<!-- Add more rows as needed -->
</tbody>
</table>
</div>
</div>
</div>
<!-- Tables Section -->
<div class="flex w-full gap-6">
<!-- Laporan Internal -->
<div class="card w-[60%] ">
<div class="card-header border-b border-green-200">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-green-500 rounded-lg flex items-center justify-center">
<i class="ki-filled ki-document text-white"></i>
</div>
<h3 class="text-lg font-semibold text-green-800">Laporan Internal</h3>
</div>
</div>
<div class="card-body">
<div class="bg-white rounded-xl overflow-hidden shadow-sm">
<table class="table table-auto w-full">
<thead class="bg-gray-50">
<tr>
<th rowspan="2"
class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider border-r border-gray-200">
Bulan
</th>
<th colspan="2"
class="px-4 py-3 text-center text-xs font-semibold text-gray-700 uppercase tracking-wider border-r border-gray-200">
Jabodetabek
</th>
<th colspan="2"
class="px-4 py-3 text-center text-xs font-semibold text-gray-700 uppercase tracking-wider">
Luar Jabodetabek
</th>
</tr>
<tr class="border-t border-gray-200">
<th
class="px-4 py-3 text-center text-xs font-semibold text-gray-700 uppercase tracking-wider border-r border-gray-200">
Laporan
</th>
<th
class="px-4 py-3 text-center text-xs font-semibold text-gray-700 uppercase tracking-wider border-r border-gray-200">
Debitur
</th>
<th
class="px-4 py-3 text-center text-xs font-semibold text-gray-700 uppercase tracking-wider border-r border-gray-200">
Laporan
</th>
<th
class="px-4 py-3 text-center text-xs font-semibold text-gray-700 uppercase tracking-wider">
Debitur
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach ($dashboard['count_lpj_internal'] as $month => $regions)
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-4 py-2 text-sm font-medium text-gray-900">
{{ ucfirst($month) }}
</td>
<td class="px-4 py-2 text-sm text-center text-gray-700">
{{ $regions['jabodetabek']['total_laporan'] ?? '-' }}
</td>
<td class="px-4 py-2 text-sm text-center text-gray-700">
{{ $regions['jabodetabek']['total_debiture'] ?? '-' }}
</td>
<td class="px-4 py-2 text-sm text-center text-gray-700">
{{ $regions['non-jabodetabek']['total_laporan'] ?? '-' }}
</td>
<td class="px-4 py-2 text-sm text-center text-gray-700">
{{ $regions['non-jabodetabek']['total_debiture'] ?? '-' }}
</td>
</tr>
@endforeach
<!-- Add more rows as needed -->
</tbody>
</table>
</div>
</div>
</div>
<!-- Tables Section -->
<div class="flex gap-6 w-full">
<!-- Laporan Internal -->
<div class="card w-[60%] ">
<div class="border-b border-green-200 card-header">
<div class="flex gap-3 items-center">
<div class="flex justify-center items-center w-10 h-10 bg-green-500 rounded-lg">
<i class="text-white ki-filled ki-document"></i>
</div>
<h3 class="text-lg font-semibold text-green-800">Laporan Internal</h3>
</div>
</div>
<div class="card-body">
<div class="overflow-hidden bg-white rounded-xl shadow-sm">
<table class="table w-full table-auto">
<thead class="bg-gray-50">
<tr>
<th rowspan="2"
class="px-4 py-3 text-xs font-semibold tracking-wider text-left text-gray-700 uppercase border-r border-gray-200">
Bulan
</th>
<th colspan="2"
class="px-4 py-3 text-xs font-semibold tracking-wider text-center text-gray-700 uppercase border-r border-gray-200">
Jabodetabek
</th>
<th colspan="2"
class="px-4 py-3 text-xs font-semibold tracking-wider text-center text-gray-700 uppercase">
Luar Jabodetabek
</th>
</tr>
<tr class="border-t border-gray-200">
<th
class="px-4 py-3 text-xs font-semibold tracking-wider text-center text-gray-700 uppercase border-r border-gray-200">
Laporan
</th>
<th
class="px-4 py-3 text-xs font-semibold tracking-wider text-center text-gray-700 uppercase border-r border-gray-200">
Debitur
</th>
<th
class="px-4 py-3 text-xs font-semibold tracking-wider text-center text-gray-700 uppercase border-r border-gray-200">
Laporan
</th>
<th
class="px-4 py-3 text-xs font-semibold tracking-wider text-center text-gray-700 uppercase">
Debitur
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach ($dashboard['count_lpj_internal'] as $month => $regions)
<tr class="transition-colors hover:bg-gray-50">
<td class="px-4 py-2 text-sm font-medium text-gray-900">
{{ ucfirst($month) }}
</td>
<td class="px-4 py-2 text-sm text-center text-gray-700">
{{ $regions['jabodetabek']['total_laporan'] ?? '-' }}
</td>
<td class="px-4 py-2 text-sm text-center text-gray-700">
{{ $regions['jabodetabek']['total_debiture'] ?? '-' }}
</td>
<td class="px-4 py-2 text-sm text-center text-gray-700">
{{ $regions['non-jabodetabek']['total_laporan'] ?? '-' }}
</td>
<td class="px-4 py-2 text-sm text-center text-gray-700">
{{ $regions['non-jabodetabek']['total_debiture'] ?? '-' }}
</td>
</tr>
@endforeach
<!-- Add more rows as needed -->
</tbody>
</table>
<!-- Laporan Eksternal -->
<div class="card w-[40%]">
<div class="card-header border-b border-purple-200">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-purple-500 rounded-lg flex items-center justify-center">
<i class="ki-filled ki-tab-tablet text-white"></i>
</div>
<h3 class="text-lg font-semibold text-purple-800">Laporan Eksternal</h3>
</div>
</div>
<div class="card-body">
<div class="bg-white rounded-xl overflow-hidden shadow-sm">
<table class="table table-auto w-full">
<thead class="bg-gray-50">
<tr>
<th
class="px-4 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider border-r border-gray-200">
Bulan
</th>
<th
class="px-4 py-3 text-center text-xs font-semibold text-gray-700 uppercase tracking-wider border-r border-gray-200">
Laporan
</th>
<th
class="px-4 py-3 text-center text-xs font-semibold text-gray-700 uppercase tracking-wider">
Debitur
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach ($dashboard['count_lpj_eksternal'] as $month => $data)
<tr class="hover:bg-gray-50 transition-colors">
<!-- Kolom Nama month -->
<td class="px-4 py-3 text-sm font-medium text-gray-900 border-r border-gray-200">
{{ ucfirst($month) }}
</td>
<!-- Laporan Eksternal -->
<div class="card w-[40%]">
<div class="border-b border-purple-200 card-header">
<div class="flex gap-3 items-center">
<div class="flex justify-center items-center w-10 h-10 bg-purple-500 rounded-lg">
<i class="text-white ki-filled ki-tab-tablet"></i>
</div>
<h3 class="text-lg font-semibold text-purple-800">Laporan Eksternal</h3>
</div>
</div>
<div class="card-body">
<div class="overflow-hidden bg-white rounded-xl shadow-sm">
<table class="table w-full table-auto">
<thead class="bg-gray-50">
<tr>
<th
class="px-4 py-3 text-xs font-semibold tracking-wider text-left text-gray-700 uppercase border-r border-gray-200">
Bulan
</th>
<th
class="px-4 py-3 text-xs font-semibold tracking-wider text-center text-gray-700 uppercase border-r border-gray-200">
Laporan
</th>
<th
class="px-4 py-3 text-xs font-semibold tracking-wider text-center text-gray-700 uppercase">
Debitur
</th>
<!-- Kolom Total Laporan -->
<td class="px-4 py-3 text-sm text-gray-700 text-center border-r border-gray-200">
{{ $data['total_laporan'] ?? '-' }}
</td>
<!-- Kolom Total Debitur -->
<td class="px-4 py-3 text-sm text-gray-700 text-center border-r border-gray-200">
{{ $data['total_debiture'] ?? '-' }}
</td>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach ($dashboard['count_lpj_eksternal'] as $month => $data)
<tr class="transition-colors hover:bg-gray-50">
<!-- Kolom Nama month -->
<td
class="px-4 py-3 text-sm font-medium text-gray-900 border-r border-gray-200">
{{ ucfirst($month) }}
</td>
<!-- Kolom Total Laporan -->
<td
class="px-4 py-3 text-sm text-center text-gray-700 border-r border-gray-200">
{{ $data['total_laporan'] ?? '-' }}
</td>
<!-- Kolom Total Debitur -->
<td
class="px-4 py-3 text-sm text-center text-gray-700 border-r border-gray-200">
{{ $data['total_debiture'] ?? '-' }}
</td>
</tr>
@endforeach
<!-- Add more rows as needed -->
</tbody>
</table>
</div>
@endforeach
<!-- Add more rows as needed -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Chart.js CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<!-- Chart.js CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<script>
function filterDashboard() {
const start_date = document.getElementById('start_date').value;
const end_date = document.getElementById('end_date').value;
window.location.href = `?start_date=${start_date}&end_date=${end_date}`;
}
</script>
<!-- Chart initialization script -->
<script>
function filterDashboard() {
const start_date = document.getElementById('start_date').value;
const end_date = document.getElementById('end_date').value;
window.location.href = `?start_date=${start_date}&end_date=${end_date}`;
}
</script>
<!-- Chart initialization script -->
@endif
@endsection

View File

@@ -4,13 +4,13 @@
@method('PUT')
@endif
@csrf
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<label class="gap-1 form-label max-w-56">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56 gap-1">
Cabang
<span class="text-danger">*</span>
</label>
<div class="flex flex-wrap items-baseline w-full">
@if(auth()->user()->hasAnyRole(['administrator','admin']))
@if(auth()->user()->hasRole('administrator'))
<select class="input tomselect w-full @error('branch_id') border-danger bg-danger-light @enderror" name="branch_id" id="branch_id">
<option value="">Pilih Cabang</option>
@foreach($branches as $branch)
@@ -21,15 +21,15 @@
</select>
@else
<input type="hidden" name="branch_id" value="{{ auth()->user()->branch_id }}">
<input type="text" class="w-full input" value="{{ auth()->user()->branch->name }}" readonly>
<input type="text" class="input w-full" value="{{ auth()->user()->branch->name }}" readonly>
@endif
@error('branch_id')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<label class="gap-1 form-label max-w-56">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56 gap-1">
CIF
<span class="text-danger">
*
@@ -38,23 +38,23 @@
<div class="flex flex-wrap items-baseline w-full">
<input class="input @error('cif') border-danger bg-danger-light @enderror" type="number" id="cif" name="cif" value="{{ $debitur->cif ?? '0000000000' }}">
@error('cif')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Nomor Rekening
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="input @error('nomor_rekening') border-danger bg-danger-light @enderror" type="number" name="nomor_rekening" value="{{ $debitur->nomor_rekening ?? '' }}">
@error('nomor_rekening')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<label class="gap-1 form-label max-w-56">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56 gap-1">
Nama Debitur
<span class="text-danger">
*
@@ -63,52 +63,52 @@
<div class="flex flex-wrap items-baseline w-full">
<input required class="input @error('name') border-danger bg-danger-light @enderror" type="text" name="name" value="{{ $debitur->name ?? '' }}">
@error('name')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
NPWP
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="input @error('npwp') border-danger bg-danger-light @enderror" type="number" name="npwp" value="{{ $debitur->npwp ?? '' }}">
@error('npwp')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Email
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="input @error('email') border-danger bg-danger-light @enderror" type="email" name="email" value="{{ $debitur->email ?? '' }}">
@error('email')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
No Handphone
</label>
<div class="flex flex-wrap items-baseline w-full">
<input class="input @error('phone') border-danger bg-danger-light @enderror" type="number" name="phone" value="{{ $debitur->phone ?? '' }}">
@error('phone')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-wrap gap-2.5 items-baseline lg:flex-nowrap">
<label class="gap-1 form-label max-w-56">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56 gap-1">
Alamat
<span class="text-danger">
*
</span>
</label>
<div class="flex flex-wrap items-baseline w-full">
<div class="flex flex-col gap-2 w-full lg:flex-row">
<div class="flex flex-col lg:flex-row gap-2 w-full">
<div class="flex flex-wrap items-baseline w-full">
<select required id="province_code" name="province_code" class="select w-full @error('province_code') border-danger bg-danger-light @enderror">
<option value="">Select Province</option>
@@ -125,7 +125,7 @@
@endforeach
</select>
@error('province_code')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
<div class="flex flex-wrap items-baseline w-full">
@@ -147,11 +147,11 @@
@endif
</select>
@error('city_code')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-col gap-2 mt-2 w-full lg:flex-row lg:mt-5">
<div class="flex flex-col lg:flex-row gap-2 w-full mt-2 lg:mt-5">
<div class="flex flex-wrap items-baseline w-full">
<select required id="district_code" name="district_code" class="select w-full @error('district_code') border-danger bg-danger-light @enderror">
<option value="">Select District</option>
@@ -171,7 +171,7 @@
@endif
</select>
@error('district_code')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
<div class="flex flex-wrap items-baseline w-full">
@@ -193,20 +193,20 @@
@endif
</select>
@error('district_code')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
<div class="flex flex-wrap items-baseline w-full">
<input required class="input @error('postal_code') border-danger bg-danger-light @enderror" type="number" id="postal_code" name="postal_code" value="{{ $debitur->postal_code ?? '' }}" placeholder="Postal Code">
@error('postal_code')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex flex-row mt-2 w-full lg:mt-5">
<div class="flex flex-row w-full mt-2 lg:mt-5">
<textarea required class="textarea @error('address') border-danger bg-danger-light @enderror" rows="3" id="address" name="address" placeholder="Alamat Lengkap">{{ $debitur->address ?? '' }}</textarea>
@error('address')
<em class="text-sm alert text-danger">{{ $message }}</em>
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>

View File

@@ -6,8 +6,8 @@
@section('content')
<div class="grid">
<div class="min-w-full border card border-agi-100 card-grid" data-datatable="false" data-datatable-page-size="10" data-datatable-state-save="false" id="debitur-table" data-api-url="{{ route('debitur.datatables') }}">
<div class="flex-wrap py-5 card-header bg-agi-50">
<div class="card border border-agi-100 card-grid min-w-full" data-datatable="false" data-datatable-page-size="10" data-datatable-state-save="false" id="debitur-table" data-api-url="{{ route('debitur.datatables') }}">
<div class="card-header bg-agi-50 py-5 flex-wrap">
<h3 class="card-title">
Daftar Debitur
</h3>
@@ -20,7 +20,7 @@
<div class="flex flex-wrap gap-2.5">
<div class="h-[24px] border border-r-gray-200"></div>
<a class="btn btn-sm btn-light" href="{{ route('debitur.export') }}"> Export to Excel </a>
@if(auth()->user()->hasAnyRole(['administrator', 'pemohon-ao','admin']))
@if(auth()->user()->hasAnyRole(['administrator', 'pemohon-ao']))
<a class="btn btn-sm btn-primary" href="{{ route('debitur.create') }}"> Tambah Debitur </a>
@endif
</div>
@@ -28,57 +28,59 @@
</div>
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
data-datatable-table="true">
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" data-datatable-table="true">
<thead>
<tr>
<th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox" />
</th>
<th class="min-w-[50px]" data-datatable-column="cif">
<span class="sort"> <span class="sort-label"> Cif </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="nomor_rekening">
<span class="sort"> <span class="sort-label"> Nomor Rekening </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="name">
<span class="sort"> <span class="sort-label"> Debitur </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="branch">
<span class="sort"> <span class="sort-label"> Cabang </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="nomor_id">
<span class="sort"> <span class="sort-label"> Nomor ID </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="npwp">
<span class="sort"> <span class="sort-label"> NPWP </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="phone">
<span class="sort"> <span class="sort-label"> Phone </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="address">
<span class="sort"> <span class="sort-label"> Alamat KTP</span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th>
</tr>
<tr>
<th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox"/>
</th>
<th class="min-w-[50px]" data-datatable-column="cif">
<span class="sort"> <span class="sort-label"> Cif </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="nomor_rekening">
<span class="sort"> <span class="sort-label"> Nomor Rekening </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="name">
<span class="sort"> <span class="sort-label"> Debitur </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="branch">
<span class="sort"> <span class="sort-label"> Cabang </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="nomor_id">
<span class="sort"> <span class="sort-label"> Nomor ID </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="npwp">
<span class="sort"> <span class="sort-label"> NPWP </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="email">
<span class="sort"> <span class="sort-label"> Email </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="phone">
<span class="sort"> <span class="sort-label"> Phone </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px]" data-datatable-column="address">
<span class="sort"> <span class="sort-label"> Alamat </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th>
</tr>
</thead>
</table>
</div>
<div
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
<div class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
<div class="flex items-center gap-2">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per page
<select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per page
</div>
<div class="flex gap-4 items-center">
<div class="flex items-center gap-4">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
@@ -95,10 +97,9 @@
</script>
<script type="text/javascript">
window.addEventListener('DOMContentLoaded', function() {
window.addEventListener('DOMContentLoaded', function () {
})
function deleteData(data) {
Swal.fire({
title: 'Are you sure?',
@@ -112,7 +113,7 @@
if (result.isConfirmed) {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
});
@@ -169,11 +170,14 @@
npwp: {
title: 'NPWP',
},
email: {
title: 'Email',
},
phone: {
title: 'Phone',
},
address: {
title: 'Alamat KTP',
title: 'Alamat',
},
actions: {
title: 'Status',
@@ -192,14 +196,14 @@
// Periksa apakah ada setidaknya satu permohonan dengan status yang valid
const hasAnyValidPermohonan = permohonanArray.some(hasValidStatus);
if (hasRole(['administrator','admin']) && hasAnyValidPermohonan || data.permohonan.length < 1) {
if (hasRole(['administrator']) && hasAnyValidPermohonan || data.permohonan.length < 1) {
actionHtml += `
<a class="btn btn-sm btn-icon btn-clear btn-info" href="/debitur/${data.id}/edit">
<i class="ki-outline ki-notepad-edit"></i>
</a>`;
}
if (hasRole(['administrator', 'pemohon-ao','admin']) && data.permohonan.length < 1) {
if (hasRole(['administrator', 'pemohon-ao']) && data.permohonan.length < 1) {
actionHtml += `
<a onclick="deleteData(${data.id})" class="delete btn btn-sm btn-icon btn-clear btn-danger">
<i class="ki-outline ki-trash"></i>
@@ -216,10 +220,11 @@
let dataTable = new KTDataTable(element, dataTableOptions);
dataTable.showSpinner();
// Custom search functionality
searchInput.addEventListener('input', function() {
searchInput.addEventListener('input', function () {
const searchValue = this.value.trim();
dataTable.search(searchValue, true);
});
</script>
@endpush

View File

@@ -44,18 +44,18 @@
}
</style>
@endpush
<div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<!-- Filter Card -->
<div class="border card border-agi-100">
<div class="py-5 card-header bg-agi-50">
<div class="card border border-agi-100">
<div class="card-header bg-agi-50 py-5">
<h3 class="card-title">Filter Laporan</h3>
</div>
<div class="grid gap-4 card-body">
<div class="card-body grid gap-4">
<!-- Search field at the top, full width -->
<div class="flex flex-col w-full">
<label class="mb-1 text-sm font-medium">Pencarian</label>
<label class="text-sm font-medium mb-1">Pencarian</label>
<label class="input input-sm">
<i class="ki-filled ki-magnifier"></i>
<input placeholder="Search Laporan Penilaian Jaminan" id="search" type="text" value="">
@@ -63,21 +63,21 @@
</div>
<!-- Other filter fields in grid layout -->
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="flex flex-col">
<label class="mb-1 text-base font-medium">Tanggal Awal</label>
<label class="text-base font-medium mb-1">Tanggal Awal</label>
<label class="input">
<input placeholder="Tanggal Awal" id="start_date" type="date">
</label>
</div>
<div class="flex flex-col">
<label class="mb-1 text-base font-medium">Tanggal Akhir</label>
<label class="text-base font-medium mb-1">Tanggal Akhir</label>
<label class="input">
<input placeholder="Tanggal Akhir" id="end_date" type="date">
</label>
</div>
<div class="flex flex-col">
<label class="mb-1 text-base font-medium">Cabang</label>
<label class="text-base font-medium mb-1">Cabang</label>
<select class="select tomselect" id="branch_filter">
<option value="">Semua Cabang</option>
@foreach (\Modules\Basicdata\Models\Branch::where('status', 1)->get() as $branch)
@@ -87,7 +87,7 @@
</div>
<div class="flex flex-col">
<label class="mb-1 text-base font-medium">Laporan</label>
<label class="text-base font-medium mb-1">Laporan</label>
<select class="select tomselect" name="laporan[]" id="laporan" multiple>
<option value="">Semua Laporan</option>
@php
@@ -103,7 +103,7 @@
</div>
<!-- Buttons row at the bottom -->
<div class="grid grid-cols-1 gap-4 mt-2 md:grid-cols-2">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-2">
<button class="btn btn-sm btn-primary" id="filter_tanggal">
<i class="ki-outline ki-filter fs-2 me-1"></i>
Terapkan Filter
@@ -117,10 +117,10 @@
</div>
<div class="min-w-full border card border-agi-100 card-grid" data-datatable="false" data-datatable-page-size="10"
<div class="card border border-agi-100 card-grid min-w-full" data-datatable="false" data-datatable-page-size="10"
data-datatable-state-save="false" id="laporan-penilai-jaminan-table"
data-api-url="{{ route('laporan-penilai-jaminan.datatables') }}">
<div class="flex-wrap py-5 card-header bg-agi-50">
<div class="card-header bg-agi-50 py-5 flex-wrap">
<h3 class="card-title">
Daftar Laporan Penilai Jaminan
</h3>
@@ -129,7 +129,7 @@
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm"
data-datatable-table="true">
<thead>
<tr>
@@ -186,13 +186,13 @@
</table>
</div>
<div
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
<div class="flex items-center gap-2">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per
<select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per
page
</div>
<div class="flex gap-4 items-center">
<div class="flex items-center gap-4">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
@@ -349,7 +349,7 @@
const dokumenjaminan = data.dokumenjaminan || [];
return `
<div class="flex flex-nowrap gap-1 justify-center">
<div class="flex flex-nowrap justify-center gap-1">
${
['done'].includes(status) ?
dokumenjaminan.map(dokumen => {

View File

@@ -1,220 +0,0 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render('admin-kredit.laporan-slik') }}
@endsection
@section('content')
<div class="grid">
<div class="min-w-full border card border-agi-100 card-grid" data-datatable="false" data-datatable-page-size="10"
data-datatable-state-save="false" id="laporan-slik-table"
data-api-url="{{ route('admin-kredit.laporan-slik.datatables') }}">
<div class="flex-wrap py-5 card-header bg-agi-50">
<h3 class="card-title">
Laporan SLIK
</h3>
<div class="flex flex-wrap gap-2 lg:gap-5">
<div class="flex">
<label class="input input-sm">
<i class="ki-filled ki-magnifier"></i>
<input placeholder="Search Laporan SLIK" id="search" type="text" value="">
</label>
</div>
<div class="flex flex-wrap gap-2.5">
<div class="h-[24px] border border-r-gray-200"></div>
<a class="btn btn-sm btn-light" href="#" id="export-excel">
<i class="ki-filled ki-file-down"></i> Export Excel
</a>
</div>
</div>
</div>
<div class="card-body">
<div class="scrollable-x-auto">
<table
class="table text-sm font-medium text-gray-700 align-middle table-auto table-border min-w-[1200px]"
data-datatable-table="true">
<thead>
<tr>
<th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox" />
</th>
<th class="min-w-[100px]" data-datatable-column="sandi_bank">
<span class="sort">
<span class="sort-label">Sandi Bank</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[120px]" data-datatable-column="no_rekening">
<span class="sort">
<span class="sort-label">No Rekening</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[120px]" data-datatable-column="baki_debet">
<span class="sort">
<span class="sort-label">Baki Debet</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[100px]" data-datatable-column="status">
<span class="sort">
<span class="sort-label">Status</span>
<span class="sort-icon"></span>
</span>
</th>
<th class="min-w-[50px] text-center" data-datatable-column="actions">Aksi</th>
</tr>
</thead>
</table>
</div>
<div
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per page
</div>
<div class="flex gap-4 items-center">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('styles')
<style>
/* Responsive adjustments for table */
@media (max-width: 768px) {
.table-responsive {
overflow-x: auto;
}
.modal-content {
max-width: 95% !important;
margin: 10px;
}
.grid-cols-1.md\:grid-cols-2 {
grid-template-columns: 1fr;
}
}
/* Ensure table is scrollable on small screens */
.scrollable-x-auto {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
/* Prevent text wrapping in table cells */
.table th,
.table td {
white-space: nowrap;
vertical-align: middle;
}
/* Allow wrapping for long text in detail modal */
.modal-body .text-gray-900 {
word-break: break-word;
}
</style>
@endpush
@push('scripts')
<script>
let dataTable;
</script>
<script type="module">
/**
* Inisialisasi DataTable untuk Laporan SLIK menggunakan KTDataTable
*/
document.addEventListener('DOMContentLoaded', function() {
const element = document.querySelector('#laporan-slik-table');
const searchInput = document.getElementById('search');
const apiUrl = element.getAttribute('data-api-url');
// Konfigurasi DataTable menggunakan KTDataTable
const dataTableOptions = {
apiEndpoint: apiUrl,
pageSize: 10,
columns: {
select: {
render: (item, data, context) => {
const checkbox = document.createElement('input');
checkbox.className = 'checkbox checkbox-sm';
checkbox.type = 'checkbox';
checkbox.value = data.id.toString();
checkbox.setAttribute('data-datatable-row-check', 'true');
return checkbox.outerHTML.trim();
},
},
sandi_bank: {
title: 'Sandi Bank',
},
no_rekening: {
title: 'No Rekening',
},
baki_debet_formatted: {
title: 'Baki Debet',
render: (item, data) => {
return data.baki_debet_formatted || '-';
},
},
status_badge: {
title: 'Status',
render: (item, data) => {
return data.status_badge || '-';
},
},
actions: {
title: 'Aksi',
render: (item, data) => {
return `
<div class="flex gap-2">
<button class="btn btn-sm btn-light btn-icon" onclick="showDetail(${data.id})" title="Detail">
<i class="ki-filled ki-eye"></i>
</button>
</div>
`;
},
}
},
};
// Inisialisasi DataTable
dataTable = new KTDataTable(element, dataTableOptions);
dataTable.showSpinner();
// Fungsi pencarian
searchInput.addEventListener('input', function() {
const searchValue = this.value.trim();
dataTable.search(searchValue, true);
});
// Export Excel functionality
document.getElementById('export-excel').addEventListener('click', function(e) {
e.preventDefault();
// Build export URL with current filters
const params = new URLSearchParams();
if (searchInput.value) params.append('search', searchInput.value);
const exportUrl = '{{ route('admin-kredit.laporan-slik.export') }}?' + params.toString();
window.open(exportUrl, '_blank');
});
});
</script>
<script>
/**
* Fungsi untuk menampilkan detail data Laporan SLIK
* @param {number} id - ID data Laporan SLIK yang akan ditampilkan
*/
function showDetail(id) {
// Redirect ke halaman detail
window.location.href = `{{ route('admin-kredit.laporan-slik.show', ':id') }}`.replace(':id', id);
}
</script>
@endpush

View File

@@ -1,172 +0,0 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render('admin-kredit.laporan-slik.show', $laporanSlik) }}
@endsection
@section('content')
<div class="grid">
<div class="w-full">
<!-- Header -->
<div class="flex flex-wrap gap-4 justify-between items-center mb-6">
<div>
<h3 class="text-xl font-semibold text-gray-900">Detail Laporan SLIK</h3>
<p class="mt-1 text-sm text-gray-600">Informasi lengkap laporan SLIK {{ $laporanSlik->nama_debitur }}
</p>
</div>
<div class="flex gap-2">
<a href="{{ route('admin-kredit.laporan-slik.index') }}" class="btn btn-sm btn-light">
<i class="ki-filled ki-arrow-left"></i>
Kembali
</a>
</div>
</div>
<!-- Card Detail -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Informasi Laporan SLIK</h3>
</div>
<div class="card-body">
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
<!-- Informasi Debitur -->
<div class="space-y-4">
<h4 class="mb-3 text-base font-semibold text-gray-800">Data Debitur</h4>
<div class="space-y-3">
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Nama Debitur:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->nama_debitur ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">No. Rekening:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->no_rekening ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">CIF:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->cif ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">NPWP:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->npwp ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">No. KTP:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->no_ktp ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">No. Telp:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->no_telp ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Alamat:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->alamat ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Kode Pos:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->kode_pos ?? '-' }}</span>
</div>
</div>
</div>
<!-- Informasi Fasilitas -->
<div class="space-y-4">
<h4 class="mb-3 text-base font-semibold text-gray-800">Data Fasilitas</h4>
<div class="space-y-3">
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Fasilitas:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->fasilitas ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Kolektibilitas:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->kolektibilitas ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Jenis Agunan:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->jenis_agunan ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Nilai Agunan:</span>
<span class="text-sm text-gray-900">Rp
{{ number_format($laporanSlik->nilai_agunan ?? 0, 0, ',', '.') }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Baki Debet:</span>
<span class="text-sm text-gray-900">Rp
{{ number_format($laporanSlik->baki_debet ?? 0, 0, ',', '.') }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Sandi Bank:</span>
<span class="text-sm text-gray-900">{{ $laporanSlik->sandi_bank ?? '-' }}</span>
</div>
<div class="flex justify-between">
<span class="text-sm font-medium text-gray-600">Status:</span>
<span class="text-sm text-gray-900">
<span
class="badge badge-light-{{ $laporanSlik->status == 'aktif' ? 'success' : 'danger' }}">
{{ $laporanSlik->status ?? 'aktif' }}
</span>
</span>
</div>
</div>
</div>
</div>
<div class="mt-4 row">
<div class="col-12">
<h5 class="mb-3">Detail Lainnya</h5>
<table class="table table-borderless">
<tr>
<td width="20%"><strong>Kolektibilitas</strong></td>
<td width="5%">:</td>
<td>{{ $laporanSlik->kolektibilitas }}</td>
<td width="20%"><strong>Tanggal Mulai</strong></td>
<td width="5%">:</td>
<td>{{ $laporanSlik->tanggal_mulai ?? '-' }}</td>
</tr>
<tr>
<td><strong>Tanggal Jatuh Tempo</strong></td>
<td>:</td>
<td>{{ $laporanSlik->tanggal_jatuh_tempo ?? '-' }}</td>
<td><strong>Tanggal Selesai</strong></td>
<td>:</td>
<td>{{ $laporanSlik->tanggal_selesai ?? '-' }}</td>
</tr>
<tr>
<td><strong>Jenis Agunan</strong></td>
<td>:</td>
<td>{{ $laporanSlik->jenis_agunan ?? '-' }}</td>
<td><strong>Fasilitas</strong></td>
<td>:</td>
<td>{{ $laporanSlik->fasilitas ?? '-' }}</td>
</tr>
<tr>
<td><strong>Tanggal Lapor</strong></td>
<td>:</td>
<td>{{ $laporanSlik->tanggal_lapor ?? '-' }}</td>
<td><strong>Tanggal Dibuat</strong></td>
<td>:</td>
<td>{{ $laporanSlik->created_at->format('d/m/Y H:i') }}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -6,63 +6,27 @@
@section('content')
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<!-- Filter Card -->
<div class="card border border-agi-100">
<div class="card-header bg-agi-50 py-5">
<h3 class="card-title"> Laporan User Penilai</h3>
</div>
<div class="card-body grid gap-4">
<!-- Search field at the top, full width -->
<div class="flex flex-col w-full">
<label class="text-sm font-medium mb-1">Pencarian</label>
<label class="input input-sm">
<i class="ki-filled ki-magnifier"></i>
<input placeholder="Search Laporan User" id="search" type="text" value="">
</label>
</div>
<!-- Other filter fields in grid layout -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="flex flex-col">
<label class="text-base font-medium mb-1">Tanggal Awal</label>
<label class="input">
<input placeholder="Tanggal Awal" id="start_date" type="date">
</label>
</div>
<div class="flex flex-col">
<label class="text-base font-medium mb-1">Tanggal Akhir</label>
<label class="input">
<input placeholder="Tanggal Akhir" id="end_date" type="date">
</label>
</div>
<div class="flex flex-col">
<label class="text-base font-medium mb-1">User</label>
<select class="select tomselect" id="user_id" name="user_id">
</select>
</div>
</div>
<!-- Buttons row at the bottom -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-2">
<button class="btn btn-sm btn-primary" id="filter_tanggal">
<i class="ki-outline ki-filter fs-2 me-1"></i>
Terapkan Filter
</button>
<a class="btn btn-sm btn-light" href="{{ route('laporan-user.export') }}" id="export-btn">
<i class="ki-outline ki-file-down fs-2 me-1"></i>
Export to Excel
</a>
</div>
</div>
</div>
<!-- Data Table Card -->
<div class="card border border-agi-100 card-grid min-w-full" data-datatable="false" data-datatable-page-size="10" data-datatable-state-save="false" id="laporan-user-table" data-api-url="{{ route('laporan-user.datatables') }}">
<div class="card-header bg-agi-50 py-5 flex-wrap">
<h3 class="card-title">
Laporan User Pemohon
Laporan User
</h3>
<div class="flex flex-wrap gap-2 lg:gap-5">
<div class="flex flex-wrap gap-2 lg:gap-5">
<label class="input input-sm">
<i class="ki-filled ki-magnifier"></i>
<input placeholder="Search Laporan User" id="search" type="text" value="">
</label>
</div>
<a class="btn btn-sm btn-light" href="{{ route('laporan-user.export') }}" id="export-btn">
<i class="ki-outline ki-file-down fs-2 me-1"></i>
Export to Excel
</a>
</div>
</div>
<div class="card-body">
@@ -73,41 +37,20 @@
<th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox"/>
</th>
<th class="min-w-[150px]" data-datatable-column="nomor_registrasi">
<span class="sort"> <span class="sort-label"> Nomor Registrasi </span>
<th class="min-w-[150px]" data-datatable-column="nik">
<span class="sort"> <span class="sort-label"> NIK</span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="branch">
<span class="sort"> <span class="sort-label"> Cabang </span>
<th class="min-w-[150px]" data-datatable-column="name">
<span class="sort"> <span class="sort-label"> Nama User</span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="name">
<span class="sort"> <span class="sort-label"> Nama Debitur </span>
<th class="min-w-[150px]" data-datatable-column="level">
<span class="sort"> <span class="sort-label"> Level Group </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="pemohon">
<span class="sort"> <span class="sort-label"> Pemohon </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="jenis_penilaian">
<span class="sort"> <span class="sort-label"> Tgl Terima </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="tujuan_penilaian">
<span class="sort"> <span class="sort-label"> Penilai </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="alamat_agunan">
<span class="sort"> <span class="sort-label"> Tgl Laporan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="catatan">
<span class="sort"> <span class="sort-label"> Nilai Pasar Wajar </span>
<th class="min-w-[150px]" data-datatable-column="approval_limit">
<span class="sort"> <span class="sort-label"> Approval Limit </span>
<span class="sort-icon"> </span> </span>
</th>
</tr>
@@ -155,40 +98,21 @@
return checkbox.outerHTML.trim();
},
},
nomor_registrasi: {
nik: {
title: 'Nomor Registrasi',
},
branch: {
title: 'Cabang',
},
name: {
title: 'Nama Debitur',
title: 'Nama',
},
pemohon: {
title: 'Pemohon',
level: {
title: 'level',
},
tanggal_permohonan: {
title: 'Tanggal Permohonan',
approval_limit: {
title: 'Approval Limit',
render: (item, data) => {
return data.tanggal_permohonan ? window.formatTanggalIndonesia(data.tanggal_permohonan) : '-';
},
},
nama_penilai: {
title: 'Nama Penilai',
},
tanggal_laporan: {
title: 'Tanggal Dokumen Diterima',
render: (item, data) => {
return data.tanggal_laporan ? window.formatTanggalIndonesia(data.tanggal_laporan) : '-';
},
},
nilai_pasar_wajar: {
title: 'Nilai Pasar Wajar',
render: (item, data) => {
return data.nilai_pasar_wajar ?? '-';
},
}
}
};
@@ -209,7 +133,6 @@
if (startDate) {
filters.start_date = startDate;
}
if (endDate) {
filters.end_date = endDate;
}
@@ -219,7 +142,6 @@
}
dataTable.search(filters);
}

View File

@@ -237,22 +237,44 @@
const type = data.penilai?.type || '';
let laporanButton = '';
let resumeButton = '';
let penyelesaian = '';
if (data.noc) {
if (!data.noc?.memo_penyelesaian) {
penyelesaian = `
<a href="{{ route('noc.index') }}/${data.noc.id}" class="btn btn-sm btn-warning">
Penyelesaian
</a>`;
}
}
if (data.penilai?.resume) {
resumeButton = `
<a href="penilai/print-out-laporan/${data.id}/${dokumenID}/${jenisJaminanID}" class="btn btn-sm btn-success">
Resume
</a>`;
<a href="penilai/print-out-laporan/${data.id}/${dokumenID}/${jenisJaminanID}" class="btn btn-sm btn-success">
Resume
</a>`;
}
if (data.status_bayar == "sudah_bayar" || data.status_bayar == "tidak_bayar") {
laporanButton = `
if ((data.status_bayar == "sudah_bayar" || data.status_bayar == "tidak_bayar")) {
if (data.penilai?.type_penilai == 'resume' && !data.penilai?.resume) {
laporanButton = `
<a href="penilai/print-out-laporan/${data.id}/${dokumenID}/${jenisJaminanID}" class="btn btn-sm btn-primary">
Laporan
</a>`;
} else {
laporanButton = `
<a href="penilai/print-out-laporan/${data.id}/${dokumenID}/${jenisJaminanID}" class="btn btn-sm btn-primary">
Laporan
</a>`;
}
}
return `<div class="flex flex-wrap gap-1.5 justify-end"> ${resumeButton} ${laporanButton} </div>`;
return `<div class="flex flex-wrap gap-1.5 justify-end"> ${penyelesaian} ${resumeButton} ${laporanButton} </div>`;
},
}
},

View File

@@ -5,123 +5,84 @@
@endsection
@section('content')
<div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<div class="border card border-agi-100">
<div class="py-5 card-header bg-agi-50">
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<div class="card border border-agi-100">
<div class="card-header bg-agi-50 py-5">
<h3 class="card-title">
Edit Laporan Admin Kredit
</h3>
</div>
<div class="card-body">
<form action="{{ route('admin-kredit.laporan.update', $laporanAdminKredit->id) }}" method="POST"
class="grid gap-5">
<form action="{{ route('laporan-admin-kredit.update', $laporanAdminKredit->id) }}" method="POST" class="grid gap-5">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 md:grid-cols-2">
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
<!-- Editable Fields -->
<div class="form-group">
<label for="kode_register_t24" class="form-label">Kode Register T24</label>
<input type="text" name="kode_register_t24" id="kode_register_t24" class="input"
value="{{ $laporanAdminKredit->kode_register_t24 }}" required>
<input type="text" name="kode_register_t24" id="kode_register_t24" class="input" value="{{ $laporanAdminKredit->kode_register_t24 }}" required>
</div>
<div class="form-group">
<label for="cif" class="form-label">CIF</label>
<input type="text" name="cif" id="cif" class="input"
value="{{ $laporanAdminKredit->debiture ? $laporanAdminKredit->debiture->cif : '' }}"
required>
<input type="text" name="cif" id="cif" class="input" value="{{ $laporanAdminKredit->debiture ? $laporanAdminKredit->debiture->cif : '' }}" required>
</div>
<!-- Read-only Information Fields -->
<div class="form-group">
<label class="form-label">Nama Debitur</label>
<div class="bg-gray-100 input">
{{ $laporanAdminKredit->debiture ? $laporanAdminKredit->debiture->name : '' }}</div>
<div class="input bg-gray-100">{{ $laporanAdminKredit->debiture ? $laporanAdminKredit->debiture->name : '' }}</div>
</div>
<div class="form-group">
<label class="form-label">Cabang</label>
<div class="bg-gray-100 input">
{{ $laporanAdminKredit->debiture ? $laporanAdminKredit->debiture->branch->name : '' }}</div>
<div class="input bg-gray-100">{{ $laporanAdminKredit->debiture ? $laporanAdminKredit->debiture->branch->name : '' }}</div>
</div>
<div class="form-group">
<label class="form-label">Jenis Agunan</label>
<div class="bg-gray-100 input">{{ $laporanAdminKredit->jenis_agunan }}</div>
</div>
<div class="form-group">
<label class="form-label">Nama Pemilik</label>
<div class="bg-gray-100 input">{{ $laporanAdminKredit->nama_pemilik }}</div>
<div class="input bg-gray-100">{{ $laporanAdminKredit->jenis_agunan }}</div>
</div>
<div class="form-group">
<label class="form-label">Bukti Kepemilikan</label>
<div class="textarea bg-gray-100 h-auto min-h-[100px] p-3">
{{ $laporanAdminKredit->bukti_kepemilikan }}</div>
<div class="input bg-gray-100">{{ $laporanAdminKredit->bukti_kepemilikan }}</div>
</div>
<div class="form-group">
<label class="form-label">Alamat Agunan</label>
<div class="textarea bg-gray-100 h-auto min-h-[100px] p-3">
{{ $laporanAdminKredit->alamat_agunan }}</div>
<div class="textarea bg-gray-100 h-auto min-h-[70px] p-3">{{ $laporanAdminKredit->alamat_agunan }}</div>
</div>
<div class="form-group">
<label class="form-label">Nama Pemilik</label>
<div class="input bg-gray-100">{{ $laporanAdminKredit->nama_pemilik }}</div>
</div>
<div class="form-group">
<label class="form-label">Tanggal Kunjungan</label>
<div class="bg-gray-100 input">
{{ \Carbon\Carbon::parse($laporanAdminKredit->tanggal_kunjungan)->format('d-m-Y') }}</div>
<div class="input bg-gray-100">{{ \Carbon\Carbon::parse($laporanAdminKredit->tanggal_kunjungan)->format('d-m-Y') }}</div>
</div>
<div class="form-group">
<label class="form-label">Nilai Pasar Wajar</label>
<div class="bg-gray-100 input">
{{ number_format($laporanAdminKredit->nilai_pasar_wajar, 0, ',', '.') }}</div>
<div class="input bg-gray-100">{{ number_format($laporanAdminKredit->nilai_pasar_wajar, 0, ',', '.') }}</div>
</div>
<div class="form-group">
<label class="form-label">Nilai Likuidasi</label>
<div class="bg-gray-100 input">
{{ number_format($laporanAdminKredit->nilai_likuidasi, 0, ',', '.') }}</div>
<div class="input bg-gray-100">{{ number_format($laporanAdminKredit->nilai_likuidasi, 0, ',', '.') }}</div>
</div>
<div class="form-group">
<label class="form-label">Nama Penilai</label>
<div class="bg-gray-100 input">{{ $laporanAdminKredit->nama_penilai }}</div>
</div>
<div class="form-group">
<label for="kolektibilitas" class="form-label">Kolektibilitas</label>
<select class="input" id="kolektibilitas" name="kolektibilitas">
<option value="">Pilih Kolektibilitas</option>
<option value="1"
{{ old('kolektibilitas', $laporanAdminKredit->kolektibilitas ?? '') == '1' ? 'selected' : '' }}>
Kolektibilitas 1 - Lancar</option>
<option value="2"
{{ old('kolektibilitas', $laporanAdminKredit->kolektibilitas ?? '') == '2' ? 'selected' : '' }}>
Kolektibilitas 2 - Dalam Perhatian Khusus</option>
<option value="3"
{{ old('kolektibilitas', $laporanAdminKredit->kolektibilitas ?? '') == '3' ? 'selected' : '' }}>
Kolektibilitas 3 - Kurang Lancar</option>
<option value="4"
{{ old('kolektibilitas', $laporanAdminKredit->kolektibilitas ?? '') == '4' ? 'selected' : '' }}>
Kolektibilitas 4 - Diragukan</option>
<option value="5"
{{ old('kolektibilitas', $laporanAdminKredit->kolektibilitas ?? '') == '5' ? 'selected' : '' }}>
Kolektibilitas 5 - Macet</option>
</select>
</div>
<div class="form-group">
<label for="keterangan" class="form-label">Keterangan</label>
<input class="textarea" id="input" name="keterangan"
placeholder="Masukkan keterangan tambahan..."
value={{ old('keterangan', $laporanAdminKredit->keterangan ?? '') }}>
<div class="input bg-gray-100">{{ $laporanAdminKredit->nama_penilai }}</div>
</div>
</div>
<div class="flex gap-3 justify-end">
<a href="{{ route('admin-kredit.laporan.index') }}" class="btn btn-light">Cancel</a>
<div class="flex justify-end gap-3">
<a href="{{ route('laporan-admin-kredit.index') }}" class="btn btn-light">Cancel</a>
<button type="submit" class="btn btn-primary">Update</button>
</div>
</form>

View File

@@ -5,11 +5,9 @@
@endsection
@section('content')
<div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<div class="min-w-full border card border-agi-100 card-grid" data-datatable="false" data-datatable-page-size="10"
data-datatable-state-save="false" id="laporan-admin-kredit-table"
data-api-url="{{ route('admin-kredit.laporan.datatables') }}">
<div class="flex-wrap py-5 card-header bg-agi-50">
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<div class="card border border-agi-100 card-grid min-w-full" data-datatable="false" data-datatable-page-size="10" data-datatable-state-save="false" id="laporan-admin-kredit-table" data-api-url="{{ route('laporan-admin-kredit.datatables') }}">
<div class="card-header bg-agi-50 py-5 flex-wrap">
<h3 class="card-title">
Daftar Laporan Admin Kredit
</h3>
@@ -34,93 +32,82 @@
</div>
<div class="flex flex-wrap gap-2.5">
<div class="h-[24px] border border-r-gray-200"></div>
<a class="btn btn-sm btn-light" href="{{ route('admin-kredit.laporan.export') }}"> Export to Excel
</a>
<a class="btn btn-sm btn-light" href="{{ route('laporan-admin-kredit.export') }}"> Export to Excel </a>
</div>
</div>
</div>
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border"
data-datatable-table="true">
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" data-datatable-table="true">
<thead>
<tr>
<th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox" />
</th>
<th class="min-w-[150px]" data-datatable-column="cif">
<span class="sort"> <span class="sort-label"> CIF </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="kode_register_t24">
<span class="sort"> <span class="sort-label"> Kode Register T24 </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="nama_debitur">
<span class="sort"> <span class="sort-label"> Nama Debitur </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="cabang">
<span class="sort"> <span class="sort-label"> Cabang </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="jenis_agunan">
<span class="sort"> <span class="sort-label"> Jenis Agunan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="bukti_kepemilikan">
<span class="sort"> <span class="sort-label"> Bukti Kepemilikan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="alamat_agunan">
<span class="sort"> <span class="sort-label"> Alamat Agunan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="nama_pemilik">
<span class="sort"> <span class="sort-label"> Nama Pemilik </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="tanggal_kunjungan">
<span class="sort"> <span class="sort-label"> Tanggal Kunjungan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="nilai_pasar_wajar">
<span class="sort"> <span class="sort-label"> Nilai Pasar Wajar </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="nilai_likuidasi">
<span class="sort"> <span class="sort-label"> Nilai Likuidasi </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="nama_penilai">
<span class="sort"> <span class="sort-label"> Nama Penilai </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="kolektibilitas">
<span class="sort"> <span class="sort-label"> Kolektibilitas </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[200px]" data-datatable-column="keterangan">
<span class="sort"> <span class="sort-label"> Keterangan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[100px]" data-datatable-column="actions">
<span class="sort"> <span class="sort-label"> Actions </span>
<span class="sort-icon"> </span> </span>
</th>
</tr>
<tr>
<th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox"/>
</th>
<th class="min-w-[150px]" data-datatable-column="cif">
<span class="sort"> <span class="sort-label"> CIF </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="kode_register_t24">
<span class="sort"> <span class="sort-label"> Kode Register T24 </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="nama_debitur">
<span class="sort"> <span class="sort-label"> Nama Debitur </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="cabang">
<span class="sort"> <span class="sort-label"> Cabang </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="jenis_agunan">
<span class="sort"> <span class="sort-label"> Jenis Agunan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="bukti_kepemilikan">
<span class="sort"> <span class="sort-label"> Bukti Kepemilikan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="alamat_agunan">
<span class="sort"> <span class="sort-label"> Alamat Agunan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="nama_pemilik">
<span class="sort"> <span class="sort-label"> Nama Pemilik </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="tanggal_kunjungan">
<span class="sort"> <span class="sort-label"> Tanggal Kunjungan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="nilai_pasar_wajar">
<span class="sort"> <span class="sort-label"> Nilai Pasar Wajar </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="nilai_likuidasi">
<span class="sort"> <span class="sort-label"> Nilai Likuidasi </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="nama_penilai">
<span class="sort"> <span class="sort-label"> Nama Penilai </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[100px]" data-datatable-column="actions">
<span class="sort"> <span class="sort-label"> Actions </span>
<span class="sort-icon"> </span> </span>
</th>
</tr>
</thead>
</table>
</div>
<div
class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
<div class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
<div class="flex items-center gap-2">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per
<select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per
page
</div>
<div class="flex gap-4 items-center">
<div class="flex items-center gap-4">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
@@ -209,24 +196,11 @@
nama_penilai: {
title: 'Nama Penilai',
},
kolektibilitas: {
title: 'Kolektibilitas',
},
keterangan: {
title: 'Keterangan',
render: (item, data) => {
// Truncate keterangan if too long
if (data.keterangan && data.keterangan.length > 50) {
return data.keterangan.substring(0, 50) + '...';
}
return data.keterangan || '-';
},
},
actions: {
title: 'Action',
render: (item, data) => {
return `<div class="flex flex-nowrap justify-center">
<a href="admin-kredit/laporan/${data.id}/edit" class="btn btn-sm btn-outline btn-info">
<a href="laporan-admin-kredit/${data.id}/edit" class="btn btn-sm btn-outline btn-info">
<i class="ki-filled ki-pencil"></i>
</a>
</div>`;

View File

@@ -98,11 +98,11 @@
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="pemohon">
<span class="sort"> <span class="sort-label"> Pemohon </span>
<span class="sort"> <span class="sort-label"> Nama Debiture </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="name">
<span class="sort"> <span class="sort-label"> Nama Debitur </span>
<span class="sort"> <span class="sort-label"> Pemohon </span>
<span class="sort-icon"> </span> </span>
</th>

View File

@@ -5,9 +5,9 @@
@endsection
@section('content')
<div class="grid gap-5 mx-auto w-full lg:gap-7.5">
<div class="min-w-full border card border-agi-100 card-grid" data-datatable="false" data-datatable-page-size="10" data-datatable-state-save="false" id="laporan-permohonan-table" data-api-url="{{ route('laporan-permohonan.data') }}">
<div class="flex-wrap py-5 card-header bg-agi-50">
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<div class="card border border-agi-100 card-grid min-w-full" data-datatable="false" data-datatable-page-size="10" data-datatable-state-save="false" id="laporan-permohonan-table" data-api-url="{{ route('laporan-permohonan.data') }}">
<div class="card-header bg-agi-50 py-5 flex-wrap">
<h3 class="card-title">
Laporan Permohonan
</h3>
@@ -56,7 +56,7 @@
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table text-sm font-medium text-gray-700 align-middle table-auto table-border" data-datatable-table="true">
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm" data-datatable-table="true">
<thead>
<tr>
<th class="w-14">
@@ -66,10 +66,6 @@
<span class="sort"> <span class="sort-label"> Nomor Registrasi </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="debiture">
<span class="sort"> <span class="sort-label"> Debitur </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="tanggal_permohonan">
<span class="sort"> <span class="sort-label"> Tanggal Permohonan </span>
<span class="sort-icon"> </span> </span>
@@ -102,13 +98,13 @@
</thead>
</table>
</div>
<div class="flex-col gap-3 justify-center font-medium text-gray-600 card-footer md:justify-between md:flex-row text-2sm">
<div class="flex gap-2 items-center">
<div class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
<div class="flex items-center gap-2">
Show
<select class="w-16 select select-sm" data-datatable-size="true" name="perpage"> </select> per
<select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per
page
</div>
<div class="flex gap-4 items-center">
<div class="flex items-center gap-4">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
@@ -148,12 +144,6 @@
nomor_registrasi: {
title: 'Nomor Registrasi',
},
debiture: {
title: 'Debitur',
render: (item, data) => {
return data.debiture ? `${data.debiture.name} ${data.debiture.cif ? `(${data.debiture.cif})` : ''}` : '';
},
},
tanggal_permohonan: {
title: 'Tanggal Permohonan',
render: (item, data) => {

Some files were not shown because too many files have changed in this diff Show More