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
213 changed files with 6553 additions and 21710 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -1,83 +0,0 @@
<?php
namespace Modules\Lpj\Helpers;
class PdfHelper
{
/**
* Format text for PDF output to handle special characters
*
* @param string $text
* @return string
*/
public static function formatText($text)
{
if (empty($text)) {
return '';
}
// Common problematic characters and their safe replacements
$replacements = [
'<' => '&lt;',
'>' => '&gt;',
'&' => '&amp;',
'"' => '&quot;',
"'" => '&#39;',
'≤' => '&le;',
'≥' => '&ge;',
'≠' => '!=',
'≈' => '~',
'×' => 'x',
'÷' => '/',
'' => '-',
'—' => '-',
'' => '"',
'' => '"',
'' => "'",
'' => "'",
];
// First pass: replace with HTML entities
$safeText = str_replace(array_keys($replacements), array_values($replacements), $text);
// Ensure UTF-8 encoding
if (!mb_check_encoding($safeText, 'UTF-8')) {
$safeText = mb_convert_encoding($safeText, 'UTF-8', 'auto');
}
// Remove any remaining non-ASCII characters that could cause issues
$safeText = preg_replace('/[^\x20-\x7E\xA0-\xFF]/', '', $safeText);
return $safeText;
}
/**
* Format mathematical symbols to text representation
*
* @param string $text
* @return string
*/
public static function formatMathSymbols($text)
{
if (empty($text)) {
return '';
}
$mathReplacements = [
'<' => 'kurang dari',
'>' => 'lebih dari',
'<=' => 'kurang dari sama dengan',
'>=' => 'lebih dari sama dengan',
'!=' => 'tidak sama dengan',
'==' => 'sama dengan',
'≤' => 'kurang dari sama dengan',
'≥' => 'lebih dari sama dengan',
'≠' => 'tidak sama dengan',
'≈' => 'kira-kira',
'≡' => 'identik dengan',
'≅' => 'hampir sama dengan',
];
return str_replace(array_keys($mathReplacements), array_values($mathReplacements), $text);
}
}

View File

@@ -1,27 +0,0 @@
<?php
namespace Modules\Lpj\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\Lpj\app\Services\ImageResizeService;
class ImageController extends Controller
{
protected $imageService;
public function __construct(ImageResizeService $imageService)
{
$this->imageService = $imageService;
}
public function show(Request $request, $path)
{
$width = $request->query('w');
$quality = $request->query('q', 80);
$resizedPath = $this->imageService->resize($path, $width, null, $quality);
return response()->file(storage_path('app/public/' . $resizedPath));
}
}

BIN
app/.DS_Store vendored

Binary file not shown.

View File

@@ -1,228 +0,0 @@
<?php
namespace Modules\Lpj\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Modules\Lpj\Jobs\CleanupInspeksiDataJob;
use Modules\Lpj\Models\Inspeksi;
use Modules\Lpj\Services\InspeksiCleanupService;
class CleanupInspeksiDataCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'lpj:cleanup-inspeksi
{--permohonan-id= : ID permohonan yang akan di-cleanup (opsional)}
{--sync : Jalankan secara synchronous}
{--dry-run : Tampilkan preview tanpa menjalankan cleanup}
{--force : Jalankan tanpa konfirmasi}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Cleanup data inspeksi lama yang tidak memiliki dokument_id';
/**
* Execute the console command.
*/
public function handle(): int
{
Log::info('CleanupInspeksiDataCommand: Memulai proses cleanup data inspeksi', [
'options' => $this->options()
]);
try {
$permohonanId = $this->option('permohonan-id');
$sync = $this->option('sync');
$dryRun = $this->option('dry-run');
$force = $this->option('force');
// Validasi opsi
if ($dryRun && $sync) {
$this->error('Opsi --dry-run dan --sync tidak dapat digunakan bersamaan.');
return Command::FAILURE;
}
// Tampilkan header
$this->info('=== Cleanup Data Inspeksi ===');
$this->newLine();
// Ambil data yang akan di-cleanup
$cleanupData = $this->getCleanupData($permohonanId);
if ($cleanupData->isEmpty()) {
$this->info('Tidak ada data yang perlu di-cleanup.');
return Command::SUCCESS;
}
// Tampilkan preview data
$this->displayPreview($cleanupData);
if ($dryRun) {
$this->info('Mode dry-run: Tidak ada perubahan yang dilakukan.');
return Command::SUCCESS;
}
// Konfirmasi jika tidak force
if (!$force && !$this->confirm('Lanjutkan dengan cleanup?')) {
$this->info('Cleanup dibatalkan.');
return Command::SUCCESS;
}
// Jalankan cleanup
$this->runCleanup($cleanupData, $sync);
$this->info('Proses cleanup selesai.');
return Command::SUCCESS;
} catch (\Exception $e) {
Log::error('CleanupInspeksiDataCommand: Terjadi error saat proses cleanup', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
$this->error('Terjadi kesalahan: ' . $e->getMessage());
return Command::FAILURE;
}
}
/**
* Ambil data yang akan di-cleanup
*/
private function getCleanupData(?int $permohonanId = null)
{
$this->info('Mencari data yang akan di-cleanup...');
$query = DB::table('inspeksi as i')
->select(
'i.permohonan_id',
'i.created_by',
DB::raw('COUNT(CASE WHEN i.dokument_id IS NOT NULL AND i.deleted_at IS NULL THEN 1 END) as new_data_count'),
DB::raw('COUNT(CASE WHEN i.dokument_id IS NULL AND i.deleted_at IS NULL THEN 1 END) as old_data_count')
)
->whereNull('i.deleted_at')
->groupBy('i.permohonan_id', 'i.created_by');
if ($permohonanId) {
$query->where('i.permohonan_id', $permohonanId);
}
$results = $query->havingRaw('new_data_count > 0 AND old_data_count > 0')
->get();
return $results;
}
/**
* Tampilkan preview data
*/
private function displayPreview($cleanupData): void
{
$this->info('Data yang akan di-cleanup:');
$this->table(
['Permohonan ID', 'Created By', 'Data Baru', 'Data Lama'],
$cleanupData->map(function ($item) {
return [
$item->permohonan_id,
$item->created_by,
$item->new_data_count,
$item->old_data_count
];
})->toArray()
);
$totalPermohonan = $cleanupData->count();
$totalOldData = $cleanupData->sum('old_data_count');
$this->info("Total permohonan: {$totalPermohonan}");
$this->info("Total data lama yang akan dihapus: {$totalOldData}");
$this->newLine();
}
/**
* Jalankan cleanup
*/
private function runCleanup($cleanupData, bool $sync): void
{
$this->info('Memulai proses cleanup...');
$this->newLine();
$progressBar = $this->output->createProgressBar($cleanupData->count());
$progressBar->setFormat('Processing: %current%/%max% [%bar%] %percent:3s%% %message%');
$progressBar->start();
$totalDeleted = 0;
$totalErrors = 0;
foreach ($cleanupData as $data) {
try {
$progressBar->setMessage("Permohonan ID: {$data->permohonan_id}");
// Ambil data baru untuk mendapatkan dokument_id
$newInspeksi = Inspeksi::where('permohonan_id', $data->permohonan_id)
->where('created_by', $data->created_by)
->whereNotNull('dokument_id')
->whereNull('deleted_at')
->first();
if (!$newInspeksi) {
$this->warn("Tidak ditemukan data baru untuk permohonan {$data->permohonan_id}");
$progressBar->advance();
continue;
}
// Jalankan cleanup
if ($sync) {
// Jalankan sync
$job = new CleanupInspeksiDataJob(
$data->permohonan_id,
$data->created_by,
$newInspeksi->dokument_id
);
$job->handle();
} else {
// Dispatch ke queue
CleanupInspeksiDataJob::dispatch(
$data->permohonan_id,
$data->created_by,
$newInspeksi->dokument_id
);
}
$totalDeleted += $data->old_data_count;
} catch (\Exception $e) {
Log::error('CleanupInspeksiDataCommand: Error pada permohonan', [
'permohonan_id' => $data->permohonan_id,
'created_by' => $data->created_by,
'error' => $e->getMessage()
]);
$this->error("Error pada permohonan {$data->permohonan_id}: {$e->getMessage()}");
$totalErrors++;
}
$progressBar->advance();
}
$progressBar->finish();
$this->newLine();
$this->newLine();
// Tampilkan hasil
$this->info('=== Hasil Cleanup ===');
$this->info("Data lama yang dihapus: {$totalDeleted}");
$this->info("Error: {$totalErrors}");
if (!$sync) {
$this->info('Job telah di-dispatch ke queue. Monitor progress di log.');
}
}
}

View File

@@ -1,192 +0,0 @@
<?php
namespace Modules\Lpj\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Modules\Lpj\Models\Inspeksi;
class CleanupInspeksiStatusCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'lpj:cleanup-inspeksi-status
{--permohonan-id= : Filter berdasarkan permohonan ID}
{--created-by= : Filter berdasarkan user ID}
{--detailed : Tampilkan detail data}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Cek status data inspeksi yang memerlukan cleanup';
/**
* Execute the console command.
*/
public function handle(): int
{
try {
$permohonanId = $this->option('permohonan-id');
$createdBy = $this->option('created-by');
$detailed = $this->option('detailed');
$this->info('=== Status Data Inspeksi ===');
$this->newLine();
// Ambil statistik umum
$this->showGeneralStats();
// Ambil data yang memerlukan cleanup
$this->showCleanupStats($permohonanId, $createdBy);
if ($detailed) {
$this->showDetailedData($permohonanId, $createdBy);
}
return Command::SUCCESS;
} catch (\Exception $e) {
Log::error('CleanupInspeksiStatusCommand: Terjadi error', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
$this->error('Terjadi kesalahan: ' . $e->getMessage());
return Command::FAILURE;
}
}
/**
* Tampilkan statistik umum
*/
private function showGeneralStats(): void
{
$this->info('Statistik Umum:');
$totalData = Inspeksi::count();
$activeData = Inspeksi::whereNull('deleted_at')->count();
$deletedData = Inspeksi::whereNotNull('deleted_at')->count();
$dataWithDokument = Inspeksi::whereNotNull('dokument_id')->count();
$dataWithoutDokument = Inspeksi::whereNull('dokument_id')->count();
$this->table(
['Metrik', 'Jumlah'],
[
['Total Data', number_format($totalData)],
['Data Aktif', number_format($activeData)],
['Data Terhapus (Soft)', number_format($deletedData)],
['Data dengan Dokument ID', number_format($dataWithDokument)],
['Data tanpa Dokument ID', number_format($dataWithoutDokument)],
]
);
$this->newLine();
}
/**
* Tampilkan statistik cleanup
*/
private function showCleanupStats(?int $permohonanId = null, ?int $createdBy = null): void
{
$this->info('Data yang Memerlukan Cleanup:');
$query = DB::table('inspeksi as i')
->select(
'i.permohonan_id',
'i.created_by',
DB::raw('COUNT(CASE WHEN i.dokument_id IS NOT NULL AND i.deleted_at IS NULL THEN 1 END) as new_data_count'),
DB::raw('COUNT(CASE WHEN i.dokument_id IS NULL AND i.deleted_at IS NULL THEN 1 END) as old_data_count'),
DB::raw('MIN(i.created_at) as oldest_data'),
DB::raw('MAX(i.created_at) as newest_data')
)
->whereNull('i.deleted_at')
->groupBy('i.permohonan_id', 'i.created_by');
if ($permohonanId) {
$query->where('i.permohonan_id', $permohonanId);
}
if ($createdBy) {
$query->where('i.created_by', $createdBy);
}
$results = $query->havingRaw('new_data_count > 0 AND old_data_count > 0')
->orderBy('old_data_count', 'desc')
->limit(20)
->get();
if ($results->isEmpty()) {
$this->info('Tidak ada data yang memerlukan cleanup.');
return;
}
$this->table(
['Permohonan ID', 'Created By', 'Data Baru', 'Data Lama', 'Data Terlama', 'Data Terbaru'],
$results->map(function ($item) {
return [
$item->permohonan_id,
$item->created_by,
$item->new_data_count,
$item->old_data_count,
$item->oldest_data,
$item->newest_data,
];
})->toArray()
);
$totalPermohonan = $results->count();
$totalOldData = $results->sum('old_data_count');
$this->info("Total permohonan yang perlu cleanup: {$totalPermohonan}");
$this->info("Total data lama yang akan dihapus: {$totalOldData}");
$this->newLine();
}
/**
* Tampilkan detail data
*/
private function showDetailedData(?int $permohonanId = null, ?int $createdBy = null): void
{
$this->info('Detail Data (20 data terbaru):');
$query = Inspeksi::with(['permohonan', 'dokument'])
->whereNull('deleted_at');
if ($permohonanId) {
$query->where('permohonan_id', $permohonanId);
}
if ($createdBy) {
$query->where('created_by', $createdBy);
}
$data = $query->orderBy('created_at', 'desc')
->limit(20)
->get();
if ($data->isEmpty()) {
$this->info('Tidak ada data untuk ditampilkan.');
return;
}
$this->table(
['ID', 'Permohonan ID', 'Created By', 'Dokument ID', 'Status', 'Created At'],
$data->map(function ($item) {
return [
$item->id,
$item->permohonan_id,
$item->created_by,
$item->dokument_id ?? '-',
$item->status,
$item->created_at,
];
})->toArray()
);
}
}

View File

@@ -1,99 +0,0 @@
<?php
namespace Modules\Lpj\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Modules\Lpj\Jobs\CleanupInspeksiDataJob;
use Modules\Lpj\Services\InspeksiCleanupService;
class CleanupSingleInspeksiCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'lpj:cleanup-single-inspeksi
{permohonan-id : ID permohonan yang akan di-cleanup}
{created-by : ID user yang membuat data}
{--sync : Jalankan secara synchronous}
{--force : Jalankan tanpa konfirmasi}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Cleanup data inspeksi untuk 1 permohonan dan user tertentu';
/**
* Execute the console command.
*/
public function handle(): int
{
Log::info('CleanupSingleInspeksiCommand: Memulai proses cleanup', [
'permohonan_id' => $this->argument('permohonan-id'),
'created_by' => $this->argument('created-by'),
'sync' => $this->option('sync')
]);
try {
$permohonanId = (int) $this->argument('permohonan-id');
$createdBy = (int) $this->argument('created-by');
$sync = $this->option('sync');
$force = $this->option('force');
// Validasi input
if ($permohonanId <= 0) {
$this->error('Permohonan ID harus angka positif.');
return Command::FAILURE;
}
if ($createdBy <= 0) {
$this->error('Created By harus angka positif.');
return Command::FAILURE;
}
// Tampilkan info
$this->info('=== Cleanup Single Inspeksi ===');
$this->info("Permohonan ID: {$permohonanId}");
$this->info("Created By: {$createdBy}");
$this->info("Mode: " . ($sync ? 'Synchronous' : 'Queue'));
$this->newLine();
// Konfirmasi jika tidak force
if (!$force && !$this->confirm('Lanjutkan dengan cleanup?')) {
$this->info('Cleanup dibatalkan.');
return Command::SUCCESS;
}
// Jalankan cleanup
$cleanupService = new InspeksiCleanupService();
if ($sync) {
$this->info('Menjalankan cleanup secara synchronous...');
$cleanupService->cleanupSync($permohonanId, $createdBy);
$this->info('Cleanup selesai.');
} else {
$this->info('Mengirim job ke queue...');
$cleanupService->cleanupAsync($permohonanId, $createdBy);
$this->info('Job telah di-dispatch ke queue.');
$this->info('Monitor progress di log.');
}
return Command::SUCCESS;
} catch (\Exception $e) {
Log::error('CleanupSingleInspeksiCommand: Terjadi error saat proses cleanup', [
'permohonan_id' => $this->argument('permohonan-id'),
'created_by' => $this->argument('created-by'),
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
$this->error('Terjadi kesalahan: ' . $e->getMessage());
return Command::FAILURE;
}
}
}

View File

@@ -1,123 +0,0 @@
# Console Commands untuk Cleanup Data Inspeksi
## Daftar Command
### 1. `lpj:cleanup-inspeksi`
Command utama untuk cleanup data inspeksi secara batch.
**Usage:**
```bash
php artisan lpj:cleanup-inspeksi [options]
```
**Options:**
- `--permohonan-id=ID` - Filter berdasarkan permohonan ID (opsional)
- `--sync` - Jalankan secara synchronous
- `--dry-run` - Tampilkan preview tanpa menjalankan cleanup
- `--force` - Jalankan tanpa konfirmasi
**Contoh Penggunaan:**
```bash
# Preview data yang akan di-cleanup
php artisan lpj:cleanup-inspeksi --dry-run
# Cleanup semua data (dengan konfirmasi)
php artisan lpj:cleanup-inspeksi
# Cleanup untuk permohonan tertentu
php artisan lpj:cleanup-inspeksi --permohonan-id=123 --force
# Jalankan secara sync
php artisan lpj:cleanup-inspeksi --sync --force
```
### 2. `lpj:cleanup-single-inspeksi`
Command untuk cleanup 1 permohonan dan user tertentu.
**Usage:**
```bash
php artisan lpj:cleanup-single-inspeksi <permohonan-id> <created-by> [options]
```
**Arguments:**
- `permohonan-id` - ID permohonan yang akan di-cleanup (required)
- `created-by` - ID user yang membuat data (required)
**Options:**
- `--sync` - Jalankan secara synchronous
- `--force` - Jalankan tanpa konfirmasi
**Contoh Penggunaan:**
```bash
# Cleanup untuk permohonan 123 oleh user 456
php artisan lpj:cleanup-single-inspeksi 123 456
# Jalankan secara sync tanpa konfirmasi
php artisan lpj:cleanup-single-inspeksi 123 456 --sync --force
```
### 3. `lpj:cleanup-inspeksi-status`
Command untuk mengecek status data inspeksi dan melihat statistik cleanup.
**Usage:**
```bash
php artisan lpj:cleanup-inspeksi-status [options]
```
**Options:**
- `--permohonan-id=ID` - Filter berdasarkan permohonan ID
- `--created-by=ID` - Filter berdasarkan user ID
- `--detailed` - Tampilkan detail data
**Contoh Penggunaan:**
```bash
# Lihat statistik umum
php artisan lpj:cleanup-inspeksi-status
# Filter berdasarkan permohonan
php artisan lpj:cleanup-inspeksi-status --permohonan-id=123
# Tampilkan detail data
php artisan lpj:cleanup-inspeksi-status --detailed
```
## Scheduling
Command cleanup otomatis dijalankan setiap hari jam 2 pagi dan setiap minggu. Konfigurasi scheduling ada di `LpjServiceProvider.php`.
## Monitoring
Semua aktivitas cleanup dicatat di log file:
- `storage/logs/laravel.log` - Log umum
- `storage/logs/cleanup-inspeksi.log` - Log cleanup harian
- `storage/logs/cleanup-inspeksi-weekly.log` - Log cleanup mingguan
## Alur Kerja Cleanup
1. **Identifikasi**: Cari data inspeksi yang memiliki:
- Data baru dengan `dokument_id` (tidak null)
- Data lama tanpa `dokument_id` (null)
- Sama `permohonan_id` dan `created_by`
2. **Proses**: Soft delete data lama menggunakan Laravel SoftDeletes
3. **Logging**: Catat setiap operasi untuk audit trail
4. **Transaction**: Gunakan DB transaction untuk konsistensi data
## Troubleshooting
### Command tidak muncul
Pastikan service provider sudah diregister dengan benar:
```bash
php artisan list | grep lpj
```
### Data tidak ter-cleanup
- Cek log untuk error
- Pastikan ada data yang memenuhi kriteria
- Gunakan `--dry-run` untuk preview
- Gunakan `--detailed` untuk melihat detail data
### Performance
Untuk data besar, gunakan mode queue (default) daripada `--sync`

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,128 +0,0 @@
<?php
namespace Modules\Lpj\Exports;
use Modules\Lpj\app\Models\ReferensiLink;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
class ReferensiLinkExport implements FromQuery, WithHeadings, WithMapping, WithStyles, ShouldAutoSize, WithColumnFormatting
{
protected $filters;
public function __construct($filters = [])
{
$this->filters = $filters;
}
/**
* Query data yang akan diexport
*/
public function query()
{
$query = ReferensiLink::with(['createdBy', 'updatedBy'])
->select('referensi_link.*');
// Apply filters
if (isset($this->filters['kategori']) && !empty($this->filters['kategori'])) {
$query->where('kategori', $this->filters['kategori']);
}
if (isset($this->filters['status']) && $this->filters['status'] !== '') {
$query->where('is_active', $this->filters['status']);
}
if (isset($this->filters['search']) && !empty($this->filters['search'])) {
$query->search($this->filters['search']);
}
return $query->ordered();
}
/**
* Header kolom
*/
public function headings(): array
{
return [
'No',
'Nama',
'Link',
'Kategori',
'Deskripsi',
'Status Aktif',
'Urutan',
'Dibuat Oleh',
'Diupdate Oleh',
'Tanggal Dibuat',
'Tanggal Diupdate',
];
}
/**
* Mapping data untuk setiap baris
*/
public function map($referensiLink): array
{
static $rowNumber = 0;
$rowNumber++;
return [
$rowNumber,
$referensiLink->name,
$referensiLink->link,
$referensiLink->kategori ?? '-',
$referensiLink->deskripsi ?? '-',
$referensiLink->is_active ? 'Aktif' : 'Tidak Aktif',
$referensiLink->urutan,
$referensiLink->createdBy ? $referensiLink->createdBy->name : '-',
$referensiLink->updatedBy ? $referensiLink->updatedBy->name : '-',
$referensiLink->created_at->format('d-m-Y H:i:s'),
$referensiLink->updated_at->format('d-m-Y H:i:s'),
];
}
/**
* Styling untuk worksheet
*/
public function styles(Worksheet $sheet)
{
return [
// Header styling
1 => [
'font' => ['bold' => true, 'size' => 12],
'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['rgb' => 'E2EFDA']],
'alignment' => ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER],
],
// Alternating row colors
'A2:K1000' => [
'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['rgb' => 'F8F9FA']],
],
];
}
/**
* Format kolom
*/
public function columnFormats(): array
{
return [
'F' => NumberFormat::FORMAT_DATE_DDMMYYYY, // Tanggal dibuat
'G' => NumberFormat::FORMAT_DATE_DDMMYYYY, // Tanggal diupdate
];
}
/**
* Custom title untuk sheet
*/
public function title(): string
{
return 'Referensi Link';
}
}

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;
@@ -20,117 +17,30 @@
use Modules\Usermanagement\Models\User;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Modules\Lpj\Services\ImageResizeService;
/**
* 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 ?? '');
@@ -188,6 +98,8 @@
$query->orWhereNull('dokumen_persetujuan');
},
)->get();
// $sql = DB::getQueryLog();
if (sizeof($query) > 0) {
$allow = false;
@@ -328,6 +240,9 @@
return $hasil;
}
// andy add
function hitungHariKerja($tanggalMulai, $tanggalSelesai)
{
$tanggalMulai = Carbon::parse($tanggalMulai)->startOfDay();
@@ -413,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;
@@ -611,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)
{
@@ -776,20 +689,3 @@
Log::error('Tidak dapat memparsing timestamp dengan format apapun: "' . $timestamp . '"');
return null;
}
if (!function_exists('resize_image')) {
/**
* Merubah ukuran gambar secara on-the-fly dan mengembalikan path-nya.
*
* @param string $path Path asli gambar.
* @param int|null $width Lebar yang diinginkan.
* @param int|null $height Tinggi yang diinginkan (opsional, akan menjaga rasio aspek jika null).
* @param int $quality Kualitas gambar (1-100).
* @return string Path gambar yang sudah di-resize.
*/
function resize_image(string $path, ?int $width, ?int $height = null, int $quality = 80): string
{
return app(ImageResizeService::class)->resize($path, $width, $height, $quality);
}
}

View File

@@ -1,32 +0,0 @@
# Run on VM
rclone hashsum MD5 "gdrive:01_Data Mentah/itdel/bawang_putih/BPM1" --drive-shared-with-me --output-file itdel_bawang_putih_gdrive.txt
rclone hashsum MD5 "gcs:transit_bucket_ysds/itdel/BPM1" --output-file itdel_bawang_putih_gcs.txt
rclone check --checksum "gcs:transit_bucket_ysds/itdel/BPM1" "gdrive:01_Data Mentah/itdel/bawang_putih/BPM1" --drive-shared-with-me --combined="itdel_bawang_putih_cheksum_bucket_drive.txt" --log-file="itdel_bawang_putih_count_checksum_bucket_drive.txt"
# Run on Local Machine
rclone hashsum MD5 "/data/storageserver/0.RAW/WGS/revio call/Raw/itdel/bawang_putih/BPM1" --output-file itdel_bawang_putih_local.txt
rclone check --checksum "/data/storageserver/0.RAW/WGS/revio call/Raw/itdel/bawang_putih/BPM1" "gdrive:01_Data Mentah/itdel/bawang_putih/BPM1" --drive-shared-with-me --combined="itdel_bawang_putih_checksum_local_gdrive.txt" --log-file="itdel_bawang_putih_count_checksum_local_gdrive.txt"
# Run on VM
rclone hashsum MD5 "gdrive:01_Data Mentah/itdel/kemenyan" --drive-shared-with-me --output-file itdel_kemenyan_gdrive.txt
rclone hashsum MD5 "gcs:transit_bucket_ysds/itdel/kemenyan" --output-file itdel_kemenyan_gcs.txt
rclone check --checksum "gcs:transit_bucket_ysds/itdel/kemenyan" "gdrive:01_Data Mentah/itdel/kemenyan" --drive-shared-with-me --combined="itdel_kemenyan_checksum_bucket_drive.txt" --log-file="itdel_kemenyan_count_checksum_bucket_drive.txt"
# Run on Local Machine
rclone hashsum MD5 "/data/storageserver/0.RAW/WGS/revio call/Raw/itdel/kemenyan" --output-file itdel_kemenyan_local.txt
rclone check --checksum "/data/storageserver/0.RAW/WGS/revio call/Raw/itdel/kemenyan" "gdrive:01_Data Mentah/itdel/kemenyan" --drive-shared-with-me --combined="itdel_kemenyan_checksum_local_gdrive.txt" --log-file="itdel_kemenyan_count_checksum_local_gdrive.txt"
# Run on VM
rclone hashsum MD5 "gdrive:01_Data Mentah/ugm/prima/bawang_putih" --drive-shared-with-me --output-file ugm_prima_bawang_putih_gdrive.txt
rclone hashsum MD5 "gcs:transit_bucket_ysds/bawang_putih_prima_UGM" --output-file ugm_prima_bawang_putih_gcs.txt
rclone check --checksum "gcs:transit_bucket_ysds/bawang_putih_prima_UGM" "gdrive:01_Data Mentah/ugm/prima/bawang_putih" --drive-shared-with-me --combined="ugm_prima_bawang_putih_checksum_bucket_drive.txt" --log-file="ugm_prima_bawang_putih_count_checksum_bucket_drive.txt"
# Run on Local Machine
rclone hashsum MD5 "/data/storageserver/0.RAW/WGS/revio call/Raw/bawang_putih" --output-file ugm_prima_bawang_putih_local.txt
rclone check --checksum "/data/storageserver/0.RAW/WGS/revio call/Raw/bawang_putih" "gdrive:01_Data Mentah/ugm/prima/bawang_putih" --drive-shared-with-me --combined="ugm_prima_bawang_putih_checksum_local_gdrive.txt" --log-file="ugm_prima_bawang_putih_count_checksum_local_gdrive.txt"
# Run on VM
rclone hashsum MD5 "gdrive:01_Data Mentah/polije/netty/bawang_merah" --drive-shared-with-me --output-file polije_netty_bawang_merah_gdrive.txt
rclone hashsum MD5 "gcs:transit_bucket_ysds/bawang_merah_netty_polije" --output-file polije_netty_bawang_merah_gcs.txt
rclone check --checksum "gcs:transit_bucket_ysds/bawang_merah_netty_polije" "gdrive:01_Data Mentah/polije/netty/bawang_merah" --drive-shared-with-me --combined="polije_netty_bawang_merah_checksum_bucket_drive.txt" --log-file="polije_netty_bawang_merah_count_checksum_bucket_drive.txt"
# Run on Local Machine
rclone hashsum MD5 "/data/storageserver/0.RAW/WGS/revio call/Raw/bawang_merah" --output-file polije_netty_bawang_merah_local.txt
rclone check --checksum "/data/storageserver/0.RAW/WGS/revio call/Raw/bawang_merah" "gdrive:01_Data Mentah/polije/netty/bawang_merah" --drive-shared-with-me --combined="polije_netty_bawang_merah_checksum_local_gdrive.txt" --log-file="polije_netty_bawang_merah_count_checksum_local_gdrive.txt"

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

@@ -3,8 +3,8 @@
namespace Modules\Lpj\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Log;
use Illuminate\Routing\Controller;
use Log;
use Modules\Location\Models\Province;
use Modules\Lpj\Http\Requests\BankDataRequest;
use Modules\Lpj\Models\BankData;
@@ -88,8 +88,7 @@ use Illuminate\Support\Facades\Log;
}
} else {
// Invalid coordinates
Log::warning("Invalid coordinates: Lat: $_lat, Lng: $_lng");
// Do something to handle this situation, such as logging an error or skipping the record
Log::warning("Invalid coordinates: Lat: $_lat, Lng: $_lng");// Do something to handle this situation, such as logging an error or skipping the record
}
}
@@ -139,8 +138,7 @@ use Illuminate\Support\Facades\Log;
}
} else {
// Invalid coordinates
Log::warning("Invalid coordinates: Lat: $lat, Lng: $lng");
// Do something to handle this situation, such as logging an error or skipping the record
Log::warning("Invalid coordinates: Lat: $lat, Lng: $lng");// Do something to handle this situation, such as logging an error or skipping the record
}
}
}
@@ -199,24 +197,6 @@ use Illuminate\Support\Facades\Log;
// Retrieve data from the database
$query = BankData::query();
// Check if show_all parameter is set
$showAll = $request->has('show_all') && $request->get('show_all') === 'true';
// If show_all is true, we'll get all data without pagination
if ($showAll) {
// Get all records without pagination
$data = $query->get();
return response()->json([
'data' => $data,
'recordsTotal' => $data->count(),
'recordsFiltered' => $data->count(),
'page' => 1,
'pageSize' => $data->count(),
'total' => 1
]);
}
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
@@ -272,10 +252,8 @@ use Illuminate\Support\Facades\Log;
// Get the total count of records
$totalRecords = $query->count();
// Apply pagination only if explicitly requested or not first load
$shouldPaginate = $request->has('page') && $request->has('size') && !$request->has('show_all');
if ($shouldPaginate) {
// 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
@@ -309,11 +287,11 @@ use Illuminate\Support\Facades\Log;
];
});
// Calculate the page count (1 if showing all data)
$pageCount = $shouldPaginate ? ceil($totalRecords / $request->get('size')) : 1;
// Calculate the page count
$pageCount = ceil($totalRecords / $request->get('size'));
// Calculate the current page number
$currentPage = $shouldPaginate ? $request->get('page', 1) : 1;
$currentPage = $request->get('page', 1);
// Ensure current page doesn't exceed page count
$currentPage = min($currentPage, $pageCount);

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

@@ -7,8 +7,6 @@ use Illuminate\Http\Request;
use Modules\Lpj\Models\CategoryDaftarPustaka;
use Modules\Lpj\Services\DaftarPustakaService;
use Modules\Lpj\Http\Requests\DaftarPustakaRequest;
use Exception;
use Illuminate\Support\Facades\Log;
class DaftarPustakaController extends Controller
{
@@ -24,15 +22,7 @@ class DaftarPustakaController extends Controller
*/
public function index(Request $request)
{
// Get categories with count of daftar pustaka
try {
$categories = CategoryDaftarPustaka::withCount('daftarPustaka')->get();
} catch (\Exception $e) {
// Handle jika tabel belum ada atau error lainnya
Log::warning('Error loading categories with count: ' . $e->getMessage());
$categories = CategoryDaftarPustaka::get(); // Fallback tanpa count
}
$categories = CategoryDaftarPustaka::all();
$daftar_pustaka = $this->daftarPustaka->getAllDaftarPustaka($request);
return view('lpj::daftar-pustaka.index', [

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

@@ -1,239 +0,0 @@
<?php
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Modules\Lpj\Models\Inspeksi;
use Modules\Lpj\Services\InspeksiCleanupService;
/**
* Controller contoh untuk menunjukkan penggunaan InspeksiCleanupService
*
* Controller ini berisi contoh method untuk membuat dan update data inspeksi
* dengan otomatis menjalankan cleanup data lama yang tidak memiliki dokument_id
*/
class InspeksiController extends Controller
{
protected InspeksiCleanupService $cleanupService;
public function __construct(InspeksiCleanupService $cleanupService)
{
$this->cleanupService = $cleanupService;
}
/**
* Contoh method untuk membuat data inspeksi baru
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function store(Request $request)
{
$validated = $request->validate([
'permohonan_id' => 'required|integer|exists:permohonan,id',
'dokument_id' => 'nullable|integer|exists:dokumen_jaminan,id',
'data_form' => 'required|json',
'foto_form' => 'nullable|json',
'denah_form' => 'nullable|json',
'name' => 'required|string|max:255',
'status' => 'required|string|max:50',
]);
DB::beginTransaction();
try {
// Buat data inspeksi baru
$inspeksi = Inspeksi::create(array_merge($validated, [
'created_by' => Auth::id(),
'updated_by' => Auth::id(),
]));
Log::info('InspeksiController: Data inspeksi berhasil dibuat', [
'inspeksi_id' => $inspeksi->id,
'permohonan_id' => $inspeksi->permohonan_id,
'dokument_id' => $inspeksi->dokument_id,
'created_by' => $inspeksi->created_by
]);
// Commit transaksi utama
DB::commit();
// Jalankan cleanup secara async jika ada dokument_id
// Ini akan menghapus data lama yang tidak memiliki dokument_id
if ($inspeksi->dokument_id) {
Log::info('InspeksiController: Memulai cleanup data lama', [
'permohonan_id' => $inspeksi->permohonan_id,
'created_by' => $inspeksi->created_by,
'dokument_id' => $inspeksi->dokument_id
]);
// Dispatch job cleanup secara async
$this->cleanupService->cleanupAsync(
$inspeksi->permohonan_id,
$inspeksi->created_by,
$inspeksi->dokument_id
);
}
return response()->json([
'success' => true,
'message' => 'Data inspeksi berhasil dibuat',
'data' => $inspeksi->load(['permohonan', 'dokument'])
], 201);
} catch (\Exception $e) {
DB::rollBack();
Log::error('InspeksiController: Gagal membuat data inspeksi', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return response()->json([
'success' => false,
'message' => 'Gagal membuat data inspeksi: ' . $e->getMessage()
], 500);
}
}
/**
* Contoh method untuk update data inspeksi
*
* @param Request $request
* @param int $id
* @return \Illuminate\Http\JsonResponse
*/
public function update(Request $request, $id)
{
$validated = $request->validate([
'dokument_id' => 'nullable|integer|exists:dokumen_jaminan,id',
'data_form' => 'required|json',
'foto_form' => 'nullable|json',
'denah_form' => 'nullable|json',
'name' => 'required|string|max:255',
'status' => 'required|string|max:50',
]);
DB::beginTransaction();
try {
$inspeksi = Inspeksi::findOrFail($id);
// Simpan data lama untuk logging
$oldDokumentId = $inspeksi->dokument_id;
// Update data
$inspeksi->update(array_merge($validated, [
'updated_by' => Auth::id(),
]));
Log::info('InspeksiController: Data inspeksi berhasil diupdate', [
'inspeksi_id' => $inspeksi->id,
'permohonan_id' => $inspeksi->permohonan_id,
'old_dokument_id' => $oldDokumentId,
'new_dokument_id' => $inspeksi->dokument_id,
'updated_by' => $inspeksi->updated_by
]);
// Commit transaksi utama
DB::commit();
// Jalankan cleanup jika dokument_id berubah dari null ke ada nilai
if (!$oldDokumentId && $inspeksi->dokument_id) {
Log::info('InspeksiController: Dokument ID berubah dari null, memulai cleanup', [
'inspeksi_id' => $inspeksi->id,
'permohonan_id' => $inspeksi->permohonan_id,
'created_by' => $inspeksi->created_by,
'new_dokument_id' => $inspeksi->dokument_id
]);
// Dispatch job cleanup secara async
$this->cleanupService->cleanupAsync(
$inspeksi->permohonan_id,
$inspeksi->created_by,
$inspeksi->dokument_id
);
}
return response()->json([
'success' => true,
'message' => 'Data inspeksi berhasil diupdate',
'data' => $inspeksi->load(['permohonan', 'dokument'])
]);
} catch (\Exception $e) {
DB::rollBack();
Log::error('InspeksiController: Gagal update data inspeksi', [
'inspeksi_id' => $id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return response()->json([
'success' => false,
'message' => 'Gagal update data inspeksi: ' . $e->getMessage()
], 500);
}
}
/**
* Contoh method untuk menjalankan cleanup secara manual
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function cleanup(Request $request)
{
$validated = $request->validate([
'permohonan_id' => 'required|integer|exists:permohonan,id',
'created_by' => 'required|integer|exists:users,id',
'sync' => 'boolean', // Opsional, default false (async)
]);
try {
$permohonanId = $validated['permohonan_id'];
$createdBy = $validated['created_by'];
$sync = $validated['sync'] ?? false;
Log::info('InspeksiController: Menjalankan cleanup manual', [
'permohonan_id' => $permohonanId,
'created_by' => $createdBy,
'sync' => $sync
]);
if ($sync) {
// Jalankan secara sync
$this->cleanupService->cleanupSync($permohonanId, $createdBy);
return response()->json([
'success' => true,
'message' => 'Cleanup selesai dijalankan secara synchronous'
]);
} else {
// Dispatch ke queue
$this->cleanupService->cleanupAsync($permohonanId, $createdBy);
return response()->json([
'success' => true,
'message' => 'Cleanup job berhasil di-dispatch ke queue'
]);
}
} catch (\Exception $e) {
Log::error('InspeksiController: Gagal menjalankan cleanup manual', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return response()->json([
'success' => false,
'message' => 'Gagal menjalankan cleanup: ' . $e->getMessage()
], 500);
}
}
}

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

@@ -1,441 +0,0 @@
<?php
namespace Modules\Lpj\Http\Controllers;
use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Lpj\Models\ReferensiLink;
use Modules\Lpj\Http\Requests\ReferensiLinkRequest;
use Modules\Lpj\Exports\ReferensiLinkExport;
use Modules\Lpj\Imports\ReferensiLinkImport;
class ReferensiLinkController extends Controller
{
public $user;
public function __construct()
{
$this->middleware('auth');
$this->middleware(function ($request, $next) {
$this->user = auth()->user();
return $next($request);
});
}
/**
* Display a listing of the resource.
*/
public function index()
{
//$this->authorize('referensi-link.view', $this->user);
$data = [
'title' => 'Referensi Link',
'subtitle' => 'Daftar Referensi Link',
'breadcrumb' => [
['url' => route('dashboard'), 'text' => 'Dashboard'],
['text' => 'Referensi Link']
],
'kategoriOptions' => $this->getKategoriOptions(),
];
return view('lpj::referensi_link.index', $data);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//$this->authorize('referensi-link.create', $this->user);
$data = [
'title' => 'Tambah Referensi Link',
'subtitle' => 'Form Tambah Referensi Link Baru',
'breadcrumb' => [
['url' => route('dashboard'), 'text' => 'Dashboard'],
['url' => route('basicdata.referensi-link.index'), 'text' => 'Referensi Link'],
['text' => 'Tambah']
],
'kategoriOptions' => $this->getKategoriOptions(),
];
return view('lpj::referensi_link.create', $data);
}
/**
* Store a newly created resource in storage.
*/
public function store(ReferensiLinkRequest $request)
{
//$this->authorize('referensi-link.create', $this->user);
try {
$validated = $request->validated();
// Set urutan otomatis jika belum diisi
if (empty($validated['urutan'])) {
$validated['urutan'] = ReferensiLink::max('urutan') + 1;
}
$referensiLink = ReferensiLink::create($validated);
return redirect()
->route('basicdata.referensi-link.index')
->with('success', 'Referensi Link berhasil ditambahkan');
} catch (Exception $e) {
return redirect()
->back()
->withInput()
->with('error', 'Gagal menambahkan Referensi Link: ' . $e->getMessage());
}
}
/**
* Show the form for editing the specified resource.
*/
public function edit($id)
{
//$this->authorize('referensi-link.update', $this->user);
$referensiLink = ReferensiLink::findOrFail($id);
$data = [
'title' => 'Edit Referensi Link',
'subtitle' => 'Form Edit Referensi Link',
'breadcrumb' => [
['url' => route('dashboard'), 'text' => 'Dashboard'],
['url' => route('basicdata.referensi-link.index'), 'text' => 'Referensi Link'],
['text' => 'Edit']
],
'referensiLink' => $referensiLink,
'kategoriOptions' => $this->getKategoriOptions(),
];
return view('lpj::referensi_link.create', $data);
}
/**
* Update the specified resource in storage.
*/
public function update(ReferensiLinkRequest $request, $id)
{
//$this->authorize('referensi-link.update', $this->user);
try {
$referensiLink = ReferensiLink::findOrFail($id);
$validated = $request->validated();
$referensiLink->update($validated);
return redirect()
->route('basicdata.referensi-link.index')
->with('success', 'Referensi Link berhasil diperbarui');
} catch (Exception $e) {
return redirect()
->back()
->withInput()
->with('error', 'Gagal memperbarui Referensi Link: ' . $e->getMessage());
}
}
/**
* Remove the specified resource from storage.
*/
public function destroy($id)
{
//$this->authorize('referensi-link.delete', $this->user);
try {
$referensiLink = ReferensiLink::findOrFail($id);
$referensiLink->delete();
return response()->json([
'success' => true,
'message' => 'Referensi Link berhasil dihapus'
]);
} catch (Exception $e) {
return response()->json([
'success' => false,
'message' => 'Gagal menghapus Referensi Link: ' . $e->getMessage()
], 500);
}
}
/**
* Datatable API for KTDataTable
*/
public function dataTable(Request $request)
{
//$this->authorize('referensi-link.view', $this->user);
$query = ReferensiLink::with(['createdBy', 'updatedBy'])
->select('referensi_link.*');
// Search
$search = $request->input('search');
if (!empty($search)) {
$query->where(function ($q) use ($search) {
$q->where('name', 'LIKE', "%{$search}%")
->orWhere('link', 'LIKE', "%{$search}%")
->orWhere('kategori', 'LIKE', "%{$search}%");
});
}
// Optional filters (support multiple request shapes)
$filters = $request->input('filters', []);
$kategori = $request->input('kategori', $filters['kategori'] ?? null);
if (!empty($kategori)) {
if (is_array($kategori)) {
$query->whereIn('kategori', $kategori);
} else {
$values = preg_split('/[,|]/', (string) $kategori, -1, PREG_SPLIT_NO_EMPTY);
if (count($values) > 1) {
$query->whereIn('kategori', $values);
} else {
$query->where('kategori', $kategori);
}
}
}
$statusRaw = $request->input('status', $filters['status'] ?? $request->input('is_active'));
$statusParsed = $this->parseActiveFilter($statusRaw);
if ($statusParsed !== null) {
$query->where('is_active', $statusParsed);
}
// Sorting
$allowedSortFields = ['id', 'name', 'link', 'kategori', 'urutan', 'is_active', 'created_at', 'updated_at'];
$sortField = in_array($request->input('sortField', 'urutan'), $allowedSortFields, true)
? $request->input('sortField', 'urutan')
: 'urutan';
$sortOrder = strtolower($request->input('sortOrder', 'asc'));
if (in_array($sortOrder, ['asc', 'desc'], true)) {
$query->orderBy($sortField, $sortOrder);
}
// Pagination
$page = max((int) $request->input('page', 1), 1);
$size = max((int) $request->input('size', 10), 1);
$totalRecords = (clone $query)->count();
$offset = ($page - 1) * $size;
$items = $query->skip($offset)->take($size)->get();
// Map data rows
$data = $items->map(function ($row) {
return [
'id' => $row->id,
'name' => $row->name,
'link' => '<a href="' . $row->link . '" target="_blank" class="text-primary">' . Str::limit($row->link, 50) . ' <i class="fas fa-external-link-alt"></i></a>',
'kategori' => $row->kategori,
'status_badge' => $row->status_badge,
'urutan' => $row->urutan,
'actions' => (
(auth()->user()->can('referensi-link.update') ? '<a class="dropdown-item" href="' . route('basicdata.referensi-link.edit', $row->id) . '"><i class="fas fa-edit"></i> Edit</a>' : '') .
(auth()->user()->can('referensi-link.delete') ? '<a class="dropdown-item text-danger" href="javascript:void(0)" onclick="deleteData(' . $row->id . ')"><i class="fas fa-trash"></i> Hapus</a>' : '')
),
];
});
return response()->json([
'draw' => (int) $request->input('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $totalRecords,
'pageCount' => (int) ceil($totalRecords / $size),
'page' => $page,
'totalCount' => $totalRecords,
'data' => $data,
]);
}
/**
* Export data to Excel
*/
public function export(Request $request)
{
//$this->authorize('referensi-link.export', $this->user);
try {
$filename = 'referensi_link_' . date('YmdHis') . '.xlsx';
return Excel::download(new ReferensiLinkExport($request->all()), $filename);
} catch (Exception $e) {
return redirect()
->back()
->with('error', 'Gagal export data: ' . $e->getMessage());
}
}
/**
* Show import form
*/
public function import()
{
//$this->authorize('referensi-link.import', $this->user);
$data = [
'title' => 'Import Referensi Link',
'subtitle' => 'Import data Referensi Link dari Excel',
'breadcrumb' => [
['url' => route('dashboard'), 'text' => 'Dashboard'],
['url' => route('basicdata.referensi-link.index'), 'text' => 'Referensi Link'],
['text' => 'Import']
],
];
return view('lpj::referensi_link.import', $data);
}
/**
* Process import
*/
public function importProcess(Request $request)
{
//$this->authorize('referensi-link.import', $this->user);
$request->validate([
'file' => 'required|mimes:xlsx,xls|max:10240', // max 10MB
]);
try {
$import = new ReferensiLinkImport();
Excel::import($import, $request->file('file'));
$stats = $import->getImportStats();
$message = "Import berhasil! {$stats['success']} data berhasil diimport";
if ($stats['failed'] > 0) {
$message .= ", {$stats['failed']} data gagal diimport";
}
return redirect()
->route('basicdata.referensi-link.index')
->with('success', $message);
} catch (Exception $e) {
Log::error('ReferensiLink import error: ' . $e->getMessage());
return redirect()
->back()
->with('error', 'Gagal import data: ' . $e->getMessage());
}
}
/**
* Toggle status (active/inactive)
*/
public function toggleStatus($id)
{
//$this->authorize('referensi-link.update', $this->user);
try {
$referensiLink = ReferensiLink::findOrFail($id);
$referensiLink->is_active = !$referensiLink->is_active;
$referensiLink->save();
return response()->json([
'success' => true,
'message' => 'Status berhasil diubah',
'status' => $referensiLink->is_active
]);
} catch (Exception $e) {
return response()->json([
'success' => false,
'message' => 'Gagal mengubah status: ' . $e->getMessage()
], 500);
}
}
/**
* Get kategori options for dropdown
*/
private function getKategoriOptions()
{
return [
'regulasi' => 'Regulasi',
'panduan' => 'Panduan',
'prosedur' => 'Prosedur',
'formulir' => 'Formulir',
'laporan' => 'Laporan',
'lainnya' => 'Lainnya'
];
}
private function parseActiveFilter($value): ?bool
{
if ($value === null || $value === '') {
return null;
}
if (is_bool($value)) {
return $value;
}
$val = strtolower(trim((string) $value));
if (in_array($val, ['1', 'true', 'aktif', 'active', 'yes', 'y'], true)) {
return true;
}
if (in_array($val, ['0', 'false', 'tidak', 'inactive', 'nonaktif', 'no', 'n'], true)) {
return false;
}
return null;
}
/**
* Download import template
*/
public function downloadTemplate()
{
//$this->authorize('referensi-link.import', $this->user);
try {
$headers = [
'Nama',
'Link',
'Kategori',
'Deskripsi',
'Status Aktif',
'Urutan'
];
$filename = 'template_referensi_link_' . date('YmdHis') . '.xlsx';
return Excel::download(new class($headers) implements \Maatwebsite\Excel\Concerns\FromArray, \Maatwebsite\Excel\Concerns\WithHeadings {
private $headers;
public function __construct($headers)
{
$this->headers = $headers;
}
public function array(): array
{
return [
['Contoh Referensi', 'https://example.com', 'panduan', 'Deskripsi contoh referensi link', 'aktif', 1],
['Contoh Regulasi', 'https://regulasi.example.com', 'regulasi', 'Deskripsi regulasi', 'aktif', 2],
];
}
public function headings(): array
{
return $this->headers;
}
}, $filename);
} catch (Exception $e) {
return redirect()
->back()
->with('error', 'Gagal download template: ' . $e->getMessage());
}
}
}

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,21 +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\Facades\Log;
use Illuminate\Support\Str;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Carbon;
use Exception;
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;
@@ -37,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;
@@ -49,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
{
@@ -108,9 +126,6 @@ class SurveyorController extends Controller
$provinces = Province::all();
$bentukTanah = BentukTanah::all();
// Jalankan cleanup inspeksi otomatis untuk permohonan ini
$this->cleanupInspeksiData($id);
// Get all inspeksi data for this permohonan
if (strtolower($permohonan->tujuanPenilaian->name) == 'rap') {
$inspeksiData = Inspeksi::where('permohonan_id', $id)
@@ -136,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'
));
}
@@ -242,7 +253,7 @@ class SurveyorController extends Controller
], 200);
} catch (\Exception $e) {
Log::error('Denah Store Error: ' . $e->getMessage());
\Log::error('Denah Store Error: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Gagal menyimpan data: ' . $e->getMessage()
@@ -783,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(),
]
);
@@ -883,10 +894,11 @@ class SurveyorController extends Controller
$penilaian = Penilaian::findOrFail($id);
$permohonan = Permohonan::where('nomor_registrasi', $penilaian->nomor_registrasi)->first();
;
if (Carbon::parse($validate['waktu_penilaian']) <= Carbon::parse($penilaian->tanggal_kunjungan)) {
return response()->json([
'success' => false,
'message' => 'Waktu penilaian harus lebih besar dari tanggal assign.'.$penilaian->tanggal_kunjungan.' '.$validate['waktu_penilaian']
'message' => 'Waktu penilaian harus lebih besar dari tanggal assign.'
], 422);
}
@@ -1067,7 +1079,7 @@ class SurveyorController extends Controller
return null;
} catch (\Exception $e) {
Log::error('File upload error: ' . $e->getMessage());
\Log::error('File upload error: ' . $e->getMessage());
throw new \Exception("Gagal mengupload file: " . $e->getMessage());
}
}
@@ -1384,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();
@@ -1840,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());
}
}
}
@@ -2109,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.');
}
@@ -2149,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'))) {
@@ -2222,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);
}
}
@@ -2380,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];
@@ -2416,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);
@@ -2711,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)
@@ -2873,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),
]);
}
@@ -2897,99 +2903,5 @@ class SurveyorController extends Controller
]);
}
/**
* Fungsi untuk cleanup data inspeksi otomatis
* Menghapus data inspeksi tanpa dokument_id jika ada data lain dengan dokument_id yang sama
*/
private function cleanupInspeksiData($permohonanId)
{
try {
Log::info('SurveyorController: Memulai cleanup inspeksi otomatis', [
'permohonan_id' => $permohonanId,
'user_id' => Auth::id()
]);
// Ambil data inspeksi yang memiliki dokument_id (data lengkap)
$dataWithDokument = Inspeksi::where('permohonan_id', $permohonanId)
->whereNotNull('dokument_id')
->whereNull('deleted_at')
->get();
// Ambil data inspeksi yang tidak memiliki dokument_id (data yang akan di-cleanup)
$dataWithoutDokument = Inspeksi::where('permohonan_id', $permohonanId)
->whereNull('dokument_id')
->whereNull('deleted_at')
->get();
// Jika ada data tanpa dokument_id, cek apakah ada data dengan dokument_id yang sama
if ($dataWithoutDokument->isNotEmpty() && $dataWithDokument->isNotEmpty()) {
// Group data dengan dokument_id by created_by
$groupedDataWithDokument = $dataWithDokument->groupBy('created_by');
// Group data tanpa dokument_id by created_by
$groupedDataWithoutDokument = $dataWithoutDokument->groupBy('created_by');
// Proses cleanup untuk setiap user
foreach ($groupedDataWithDokument as $userId => $userDataWithDokument) {
// Cek apakah user ini juga memiliki data tanpa dokument_id
if (isset($groupedDataWithoutDokument[$userId])) {
// Ambil salah satu data dengan dokument_id sebagai referensi untuk logging
$referenceData = $userDataWithDokument->first();
Log::info('SurveyorController: Menemukan data lengkap untuk user, akan menghapus data tidak lengkap', [
'user_id' => $userId,
'permohonan_id' => $permohonanId,
'reference_dokument_id' => $referenceData->dokument_id,
'data_count_to_delete' => $groupedDataWithoutDokument[$userId]->count()
]);
// Ambil semua data tanpa dokument_id untuk user ini
$userDataWithoutDokument = $groupedDataWithoutDokument[$userId];
// Soft delete data tanpa dokument_id
foreach ($userDataWithoutDokument as $dataToDelete) {
DB::beginTransaction();
try {
// Soft delete data
$dataToDelete->delete();
Log::info('SurveyorController: Data inspeksi berhasil di-soft delete', [
'id' => $dataToDelete->id,
'permohonan_id' => $dataToDelete->permohonan_id,
'dokument_id' => $dataToDelete->dokument_id,
'created_by' => $dataToDelete->created_by,
'deleted_at' => now()
]);
DB::commit();
} catch (\Exception $e) {
DB::rollback();
Log::error('SurveyorController: Gagal menghapus data inspeksi', [
'id' => $dataToDelete->id,
'error' => $e->getMessage()
]);
}
}
}
}
}
Log::info('SurveyorController: Cleanup inspeksi otomatis selesai', [
'permohonan_id' => $permohonanId,
'data_with_dokument' => $dataWithDokument->count(),
'data_without_dokument' => $dataWithoutDokument->count()
]);
} catch (\Exception $e) {
Log::error('SurveyorController: Error saat cleanup inspeksi otomatis', [
'permohonan_id' => $permohonanId,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
}
}
}

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,114 +0,0 @@
<?php
namespace Modules\Lpj\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ReferensiLinkRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules(): array
{
$rules = [
'name' => 'required|string|max:255',
'link' => 'required|string|max:500',
'kategori' => 'nullable|string|max:100',
'deskripsi' => 'nullable|string|max:2000',
'is_active' => 'boolean',
'urutan' => 'nullable|integer|min:0',
];
// Validasi tambahan untuk link
$rules['link'] .= '|url';
return $rules;
}
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return true;
}
/**
* Get custom messages for validator errors.
*
* @return array<string, string>
*/
public function messages(): array
{
return [
'name.required' => 'Nama referensi link wajib diisi',
'name.string' => 'Nama referensi link harus berupa teks',
'name.max' => 'Nama referensi link maksimal 255 karakter',
'link.required' => 'Link wajib diisi',
'link.string' => 'Link harus berupa teks',
'link.max' => 'Link maksimal 500 karakter',
'link.url' => 'Link harus berupa URL yang valid',
'kategori.string' => 'Kategori harus berupa teks',
'kategori.max' => 'Kategori maksimal 100 karakter',
'deskripsi.string' => 'Deskripsi harus berupa teks',
'deskripsi.max' => 'Deskripsi maksimal 2000 karakter',
'is_active.boolean' => 'Status aktif harus berupa ya/tidak',
'urutan.integer' => 'Urutan harus berupa angka',
'urutan.min' => 'Urutan minimal 0',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array<string, string>
*/
public function attributes(): array
{
return [
'name' => 'Nama Referensi Link',
'link' => 'Link',
'kategori' => 'Kategori',
'deskripsi' => 'Deskripsi',
'is_active' => 'Status Aktif',
'urutan' => 'Urutan',
];
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
// Format link jika belum memiliki protocol
if ($this->has('link')) {
$link = $this->input('link');
if ($link && !preg_match('/^(https?:\/\/)/i', $link)) {
$this->merge([
'link' => 'https://' . $link
]);
}
}
// Set default is_active jika tidak diset
if (!$this->has('is_active')) {
$this->merge([
'is_active' => true
]);
}
// Set default urutan jika tidak diset atau 0
if (!$this->has('urutan') || empty($this->input('urutan'))) {
$this->merge([
'urutan' => 0
]);
}
}
}

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,166 +0,0 @@
<?php
namespace Modules\Lpj\Imports;
use Modules\Lpj\app\Models\ReferensiLink;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\SkipsFailures;
use Maatwebsite\Excel\Concerns\SkipsOnFailure;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class ReferensiLinkImport implements ToModel, WithHeadingRow, WithValidation, SkipsOnFailure, WithBatchInserts, WithChunkReading
{
use Importable, SkipsFailures;
protected $importedCount = 0;
protected $failedCount = 0;
protected $errors = [];
/**
* Convert row to model
*/
public function model(array $row)
{
try {
$this->importedCount++;
return new ReferensiLink([
'name' => $row['nama'] ?? $row['name'],
'link' => $this->formatLink($row['link'] ?? $row['url'] ?? ''),
'kategori' => $row['kategori'] ?? $row['category'] ?? 'lainnya',
'deskripsi' => $row['deskripsi'] ?? $row['description'] ?? null,
'is_active' => $this->parseStatus($row['status_aktif'] ?? $row['is_active'] ?? 'aktif'),
'urutan' => $this->parseUrutan($row['urutan'] ?? $row['order'] ?? 0),
'created_by' => Auth::id(),
'updated_by' => Auth::id(),
]);
} catch (\Exception $e) {
$this->failedCount++;
$this->errors[] = "Baris {$row['row_number']}: " . $e->getMessage();
Log::error('Import ReferensiLink Error: ' . $e->getMessage(), ['row' => $row]);
return null;
}
}
/**
* Validation rules
*/
public function rules(): array
{
return [
'nama' => 'required|string|max:255',
'name' => 'required|string|max:255',
'link' => 'required|string|max:500',
'url' => 'required|string|max:500',
'kategori' => 'nullable|string|max:100',
'category' => 'nullable|string|max:100',
'deskripsi' => 'nullable|string|max:2000',
'description' => 'nullable|string|max:2000',
'status_aktif' => 'nullable|string|in:aktif,tidak aktif,1,0,true,false',
'is_active' => 'nullable|string|in:aktif,tidak aktif,1,0,true,false',
'urutan' => 'nullable|integer|min:0',
'order' => 'nullable|integer|min:0',
];
}
/**
* Custom validation messages
*/
public function customValidationMessages()
{
return [
'nama.required' => 'Nama wajib diisi',
'name.required' => 'Nama wajib diisi',
'link.required' => 'Link wajib diisi',
'url.required' => 'Link wajib diisi',
'link.url' => 'Link harus berupa URL yang valid',
'url.url' => 'Link harus berupa URL yang valid',
'kategori.max' => 'Kategori maksimal 100 karakter',
'category.max' => 'Kategori maksimal 100 karakter',
'deskripsi.max' => 'Deskripsi maksimal 2000 karakter',
'description.max' => 'Deskripsi maksimal 2000 karakter',
];
}
/**
* Batch size for inserts
*/
public function batchSize(): int
{
return 100;
}
/**
* Chunk size for reading
*/
public function chunkSize(): int
{
return 100;
}
/**
* Format link to ensure it has protocol
*/
private function formatLink($link)
{
if (empty($link)) {
return null;
}
// Remove any whitespace
$link = trim($link);
// Add protocol if not present
if (!preg_match('/^(https?:\/\/)/i', $link)) {
$link = 'https://' . $link;
}
return $link;
}
/**
* Parse status value
*/
private function parseStatus($status)
{
if (empty($status)) {
return true;
}
$status = strtolower(trim($status));
return in_array($status, ['aktif', '1', 'true', 'yes', 'ya']);
}
/**
* Parse order value
*/
private function parseUrutan($urutan)
{
if (empty($urutan)) {
return 0;
}
return (int) $urutan;
}
/**
* Get import statistics
*/
public function getImportStats(): array
{
return [
'imported' => $this->importedCount,
'failed' => $this->failedCount,
'errors' => $this->errors,
'success' => $this->importedCount - $this->failedCount,
];
}
}

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,156 +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\DB;
use Illuminate\Support\Facades\Log;
use Modules\Lpj\Models\Inspeksi;
/**
* Job untuk membersihkan data inspeksi yang tidak memiliki dokument_id
*
* Case: Jika ada data inspeksi yang masuk dengan permohonan_id yang sama
* tetapi memiliki dokument_id dan user created_by yang sama, maka
* data lama (tanpa dokument_id) akan di-soft delete
*/
class CleanupInspeksiDataJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 300; // 5 menit
public $tries = 3;
public $maxExceptions = 3;
public $backoff = [60, 120, 300]; // Exponential backoff dalam detik
protected int $permohonanId;
protected int $createdBy;
protected ?int $dokumentId;
/**
* Create a new job instance.
*
* @param int $permohonanId
* @param int $createdBy
* @param int|null $dokumentId
*/
public function __construct(int $permohonanId, int $createdBy, ?int $dokumentId = null)
{
$this->permohonanId = $permohonanId;
$this->createdBy = $createdBy;
$this->dokumentId = $dokumentId;
}
/**
* Execute the job.
*
* @return void
* @throws \Exception
*/
public function handle(): void
{
Log::info('CleanupInspeksiDataJob: Memulai proses cleanup data inspeksi', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy,
'dokument_id' => $this->dokumentId
]);
DB::beginTransaction();
try {
// Cari data inspeksi yang memiliki dokument_id (data baru)
$newInspeksi = Inspeksi::where('permohonan_id', $this->permohonanId)
->where('created_by', $this->createdBy)
->whereNotNull('dokument_id')
->whereNull('deleted_at')
->first();
if (!$newInspeksi) {
Log::warning('CleanupInspeksiDataJob: Tidak ditemukan data inspeksi baru dengan dokument_id', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy
]);
DB::rollBack();
return;
}
Log::info('CleanupInspeksiDataJob: Data inspeksi baru ditemukan', [
'inspeksi_id' => $newInspeksi->id,
'dokument_id' => $newInspeksi->dokument_id
]);
// Cari data inspeksi lama yang tidak memiliki dokument_id
$oldInspeksiList = Inspeksi::where('permohonan_id', $this->permohonanId)
->where('created_by', $this->createdBy)
->whereNull('dokument_id')
->whereNull('deleted_at')
->where('id', '!=', $newInspeksi->id) // Jangan hapus data yang baru saja ditemukan
->get();
if ($oldInspeksiList->isEmpty()) {
Log::info('CleanupInspeksiDataJob: Tidak ditemukan data inspeksi lama tanpa dokument_id', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy
]);
DB::commit();
return;
}
$deletedCount = 0;
foreach ($oldInspeksiList as $oldInspeksi) {
// Soft delete data lama
$oldInspeksi->delete(); // Menggunakan soft delete karena model menggunakan SoftDeletes trait
Log::info('CleanupInspeksiDataJob: Data inspeksi lama berhasil di-soft delete', [
'old_inspeksi_id' => $oldInspeksi->id,
'permohonan_id' => $oldInspeksi->permohonan_id,
'created_by' => $oldInspeksi->created_by,
'deleted_at' => now()->toDateTimeString()
]);
$deletedCount++;
}
DB::commit();
Log::info('CleanupInspeksiDataJob: Proses cleanup selesai', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy,
'deleted_count' => $deletedCount,
'new_inspeksi_id' => $newInspeksi->id
]);
} catch (\Exception $e) {
DB::rollBack();
Log::error('CleanupInspeksiDataJob: Terjadi error saat proses cleanup', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw $e;
}
}
/**
* Handle a job failure.
*
* @param \Throwable $exception
* @return void
*/
public function failed(\Throwable $exception): void
{
Log::error('CleanupInspeksiDataJob: Job gagal dieksekusi', [
'permohonan_id' => $this->permohonanId,
'created_by' => $this->createdBy,
'error' => $exception->getMessage(),
'trace' => $exception->getTraceAsString()
]);
}
}

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

@@ -21,7 +21,7 @@ class CategoryDaftarPustaka extends Model
];
public function daftarPustaka(){
return $this->hasMany(DaftarPustaka::class, 'category_id');
return $this->hasMany(DaftarPustaka::class);
}
}

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,152 +0,0 @@
<?php
namespace Modules\Lpj\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Facades\Auth;
use Modules\Usermanagement\Models\User;
class ReferensiLink extends Model
{
use HasFactory;
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'referensi_link';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'link',
'kategori',
'deskripsi',
'is_active',
'urutan',
'created_by',
'updated_by',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'is_active' => 'boolean',
'urutan' => 'integer',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**
* Boot the model.
*/
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
if (Auth::check()) {
$model->created_by = Auth::id();
$model->updated_by = Auth::id();
}
});
static::updating(function ($model) {
if (Auth::check()) {
$model->updated_by = Auth::id();
}
});
}
/**
* Scope untuk filter data aktif
*/
public function scopeActive($query)
{
return $query->where('is_active', true);
}
/**
* Scope untuk filter berdasarkan kategori
*/
public function scopeByKategori($query, $kategori)
{
return $query->where('kategori', $kategori);
}
/**
* Scope untuk urutkan berdasarkan urutan
*/
public function scopeOrdered($query)
{
return $query->orderBy('urutan', 'asc')->orderBy('name', 'asc');
}
/**
* Scope untuk pencarian
*/
public function scopeSearch($query, $search)
{
return $query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('link', 'like', "%{$search}%")
->orWhere('kategori', 'like', "%{$search}%")
->orWhere('deskripsi', 'like', "%{$search}%");
});
}
/**
* Relasi ke user yang membuat
*/
public function createdBy()
{
return $this->belongsTo(User::class, 'created_by');
}
/**
* Relasi ke user yang update
*/
public function updatedBy()
{
return $this->belongsTo(User::class, 'updated_by');
}
/**
* Accessor untuk status badge
*/
public function getStatusBadgeAttribute()
{
return $this->is_active
? '<span class="badge bg-success">Aktif</span>'
: '<span class="badge bg-danger">Tidak Aktif</span>';
}
/**
* Accessor untuk link yang diformat
*/
public function getFormattedLinkAttribute()
{
return $this->link ? url($this->link) : null;
}
/**
* Mutator untuk memastikan link valid
*/
public function setLinkAttribute($value)
{
// Validasi dan format link
if ($value && !preg_match('/^(https?:\/\/)/i', $value)) {
$value = 'https://' . $value;
}
$this->attributes['link'] = $value;
}
}

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

@@ -32,46 +32,25 @@
}
/**
* Register commands in the format of Command::class
*/
protected function registerCommands()
: void
{
$this->commands([
\Modules\Lpj\Console\Commands\CleanupInspeksiDataCommand::class,
\Modules\Lpj\Console\Commands\CleanupSingleInspeksiCommand::class,
\Modules\Lpj\Console\Commands\CleanupInspeksiStatusCommand::class,
]);
}
* Register commands in the format of Command::class
*/
protected function registerCommands()
: void
{
// $this->commands([]);
}
/**
* Register command Schedules.
*/
protected function registerCommandSchedules()
: void
{
$this->app->booted(function () {
$schedule = $this->app->make(\Illuminate\Console\Scheduling\Schedule::class);
// Jalankan cleanup inspeksi setiap hari jam 2 pagi
$schedule->command('lpj:cleanup-inspeksi --force')
->dailyAt('02:00')
->withoutOverlapping()
->onOneServer()
->runInBackground()
->appendOutputTo(storage_path('logs/cleanup-inspeksi.log'));
// Backup cleanup setiap minggu
$schedule->command('lpj:cleanup-inspeksi --force')
->weekly()
->sundays()
->at('03:00')
->withoutOverlapping()
->onOneServer()
->runInBackground()
->appendOutputTo(storage_path('logs/cleanup-inspeksi-weekly.log'));
});
}
* Register command Schedules.
*/
protected function registerCommandSchedules()
: void
{
// $this->app->booted(function () {
// $schedule = $this->app->make(Schedule::class);
// $schedule->command('inspire')->hourly();
// });
}
/**
* Register translations.

View File

@@ -1,73 +0,0 @@
<?php
namespace Modules\Lpj\Services;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Laravel\Facades\Image;
class ImageResizeService
{
/**
* Resize an image and store it.
*
* @param string $originalPath
* @param int $width
* @param int|null $height
* @param int $quality
* @return string
*/
public function resize(string $originalPath, ?int $width, ?int $height = null, int $quality = 80): string
{
if (empty($originalPath) || !Storage::disk('public')->exists($originalPath)) {
Log::warning("Image Service: Original path is empty or does not exist: {$originalPath}");
return '';
}
$height = null;
$pathinfo = pathinfo($originalPath);
// Kembali menggunakan direktori 'resized' dan menyertakan dimensi di nama file
$resizedPath = "{$pathinfo['dirname']}/resized/{$pathinfo['filename']}_{$width}x{$height}_{$quality}.{$pathinfo['extension']}";
if (Storage::disk('public')->exists($resizedPath)) {
return $resizedPath;
}
try {
$originalFullPath = Storage::disk('public')->path($originalPath);
$newFullPath = Storage::disk('public')->path($resizedPath);
$image = Image::read($originalFullPath);
// Resize dengan menjaga aspek rasio jika height null
if ($width && $height) {
// Paksa resize ke dimensi yang ditentukan, abaikan aspek rasio
$image->resize($width, $height);
} elseif ($width && !$height) {
// Resize hanya berdasarkan width, tinggi menyesuaikan aspek rasio
$image->scale(width: $width);
} elseif (!$width && $height) {
// Resize hanya berdasarkan height, lebar menyesuaikan aspek rasio
$image->scale(height: $height);
}
if (!Storage::disk('public')->exists(dirname($resizedPath))) {
Storage::disk('public')->makeDirectory(dirname($resizedPath));
}
// Simpan dengan kualitas yang ditentukan
$image->save($newFullPath, $quality);
Log::info("Image Service: Successfully resized {$originalPath} to {$resizedPath} with quality {$quality}%.");
return $resizedPath;
} catch (\Exception $e) {
Log::error("Image Service: Resize failed for {$originalPath}. Error: " . $e->getMessage());
return $originalPath; // Fallback ke gambar asli jika gagal
}
}
}

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,83 +0,0 @@
<?php
namespace Modules\Lpj\Services;
use Illuminate\Support\Facades\Log;
use Modules\Lpj\Jobs\CleanupInspeksiDataJob;
/**
* Service untuk membersihkan data inspeksi yang tidak memiliki dokument_id
*
* Class ini menyediakan method untuk menjalankan cleanup data inspeksi
* ketika ada data baru dengan dokument_id yang sama
*/
class InspeksiCleanupService
{
/**
* Dispatch job untuk cleanup data inspeksi
*
* @param int $permohonanId
* @param int $createdBy
* @param int|null $dokumentId
* @param bool $sync
* @return void
*/
public function cleanupInspeksiData(int $permohonanId, int $createdBy, ?int $dokumentId = null, bool $sync = false): void
{
Log::info('InspeksiCleanupService: Memulai cleanup data inspeksi', [
'permohonan_id' => $permohonanId,
'created_by' => $createdBy,
'dokument_id' => $dokumentId,
'sync' => $sync
]);
try {
$job = new CleanupInspeksiDataJob($permohonanId, $createdBy, $dokumentId);
if ($sync) {
// Jalankan secara synchronous (langsung)
$job->handle();
Log::info('InspeksiCleanupService: Cleanup selesai dijalankan secara sync');
} else {
// Dispatch ke queue
dispatch($job);
Log::info('InspeksiCleanupService: Cleanup job berhasil di-dispatch ke queue');
}
} catch (\Exception $e) {
Log::error('InspeksiCleanupService: Gagal menjalankan cleanup', [
'permohonan_id' => $permohonanId,
'created_by' => $createdBy,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw $e;
}
}
/**
* Dispatch job untuk cleanup data inspeksi secara async (default)
*
* @param int $permohonanId
* @param int $createdBy
* @param int|null $dokumentId
* @return void
*/
public function cleanupAsync(int $permohonanId, int $createdBy, ?int $dokumentId = null): void
{
$this->cleanupInspeksiData($permohonanId, $createdBy, $dokumentId, false);
}
/**
* Jalankan cleanup data inspeksi secara sync
*
* @param int $permohonanId
* @param int $createdBy
* @param int|null $dokumentId
* @return void
*/
public function cleanupSync(int $permohonanId, int $createdBy, ?int $dokumentId = null): void
{
$this->cleanupInspeksiData($permohonanId, $createdBy, $dokumentId, true);
}
}

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)) {
@@ -347,7 +345,6 @@ class PreviewLaporanService
$pdf->setPaper('A4', 'portrait');
$pdf->set_option('isHtml5ParserEnabled', true);
$pdf->set_option('isPhpEnabled', true);
$pdf->set_option('dpi', '96');
return $pdf;
}

View File

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

View File

@@ -1,75 +0,0 @@
🐛 fix(lpj): Perbaikan format Rupiah, role access, dan validasi data
## Ringkasan
Melakukan perbaikan pada helper format Rupiah, akses role user, validasi data MIG, serta penyesuaian tampilan laporan dan dokumentasi.
## Perubahan Detail
### 🔧 Helper Function
**app/Helpers/Lpj.php**:
- Menambahkan parameter opsional `withSymbol` pada fungsi `formatRupiah()` untuk kontrol simbol Rp
- Menambahkan handling untuk menghapus titik (.) dari input number sebelum proses
- Memperbaiki return value untuk null/empty string sesuai parameter `withSymbol`
- Mengganti `str_pad()` dengan `sprintf()` untuk generate random number (lebih efisien)
### 🛠️ Service Layer
**app/Services/PreviewLaporanService.php**:
- Memperbaiki validasi data MIG dengan menambahkan pengecekan `is_mig` flag
- Menambahkan null safety pada property `mig_mst_lpj_tot_nilai_pasar`
- Memperbaiki kondisi logic untuk memo dan validasi nilai pasar
### 🎨 View Components
**resources/views/component/print-out-dokument.blade.php**:
- Memperbaiki syntax Blade dari `@isset` menjadi `isset()` yang lebih proper
**resources/views/debitur/components/debitur.blade.php**:
- Memperbaiki role checking dari `hasRole()` menjadi `hasAnyRole()` untuk multiple roles
**resources/views/debitur/index.blade.php**:
- Menambahkan role 'admin' pada kondisi edit dan delete actions
- Memperbaiki permission checking untuk administrator dan admin
**resources/views/laporan/index.blade.php**:
- Menyederhanakan logic tombol laporan dan resume
- Menghapus logic role-based yang kompleks untuk tombol laporan
- Memperbaiki route URL untuk print-out laporan
- Menghapus function `generateLaporanButton()` yang tidak digunakan
**resources/views/penilai/components/lpj-sederhana-standar.blade.php**:
- Menambahkan role 'penilai' pada permission tombol simpan
**resources/views/penilai/components/print-out-sederhana.blade.php**:
- Memperbaiki tampilan data dokumen dengan menambahkan kolom nomor dokumen
- Mengganti `number_format()` dengan `formatRupiah()` untuk konsistensi format
- Menambahkan fallback untuk data tanah dan bangunan ketika `npw_tambahan` tidak tersedia
- Memperbaiki perhitungan total nilai pasar wajar dengan proper parsing
- Memperbaiki format tampilan nilai likuidasi
- Memperbaiki struktur HTML tabel untuk dokumentasi
**resources/views/penilai/components/signature-approval.blade.php**:
- Memperbaiki route dan parameter untuk approval signature
**resources/views/permohonan/index.blade.php**:
- Menambahkan role 'admin' pada permission actions
## Alasan Perubahan
1. **Format Rupiah**: Menambahkan fleksibilitas untuk menampilkan nominal dengan atau tanpa simbol Rp sesuai kebutuhan tampilan
2. **Validasi Data**: Memperkuat validasi data MIG untuk mencegah error pada data yang tidak lengkap
3. **Role Access**: Memperbaiki permission checking untuk mencakup role admin yang sebelumnya terlewat
4. **Tampilan Laporan**: Menyederhanakan UI dan memperbaiki format tampilan nilai untuk konsistensi
5. **Fallback Data**: Menambahkan handling untuk kasus data tidak lengkap pada laporan penilaian
## Dampak
- ✅ Format Rupiah lebih fleksibel dengan opsi simbol
- ✅ Validasi data MIG lebih kuat dan aman
- ✅ Role admin sekarang memiliki akses yang sesuai
- ✅ Tampilan laporan lebih konsisten dan rapi
- ✅ Penanganan error untuk data tidak lengkap lebih baik
## Testing
Pastikan untuk:
1. Test format Rupiah dengan berbagai skenario (dengan/ tanpa simbol)
2. Test akses role admin pada semua fitur yang diperbarui
3. Test validasi data MIG dengan data lengkap dan tidak lengkap
4. Test tampilan laporan dengan data npw_tambahan kosong
5. Verifikasi perhitungan total nilai pasar wajar tetap akurat

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),
],
],
];

BIN
database/.DS_Store vendored

Binary file not shown.

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

@@ -1,45 +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('referensi_link', function (Blueprint $table) {
$table->id();
$table->string('name', 255)->nullable(false)->comment('Nama referensi link');
$table->text('link')->nullable(false)->comment('URL link referensi');
$table->string('kategori', 100)->nullable()->comment('Kategori referensi (misal: regulasi, panduan, dll)');
$table->text('deskripsi')->nullable()->comment('Deskripsi lengkap referensi');
$table->boolean('is_active')->default(true)->comment('Status aktif referensi');
$table->integer('urutan')->default(0)->comment('Urutan tampil referensi');
$table->unsignedBigInteger('created_by')->nullable()->comment('ID user yang membuat');
$table->unsignedBigInteger('updated_by')->nullable()->comment('ID user yang update terakhir');
$table->timestamps();
// Indexes
$table->index('kategori');
$table->index('is_active');
$table->index('urutan');
$table->index('created_by');
// Foreign keys
$table->foreign('created_by')->references('id')->on('users')->onDelete('set null');
$table->foreign('updated_by')->references('id')->on('users')->onDelete('set null');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('referensi_link');
}
};

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([]);
}
}

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