✨ feat(lpj-seeders): Inisialisasi master referensi, migrasi MIG, dan SQL seeding
- Menata LpjDatabaseSeeder untuk orkestrasi batch, aktifkan MigrationGambarInspeksiSeeder . - Migrasi domain MIG → LPJ lengkap dengan parseTimestamp , initializeErrorLog , logError . - Tambah seeders MIG eksternal & tim penilai; normalisasi mapping checkTujuanPenilaian . - Perluasan master: JFK009–JFK014, TP0007–TP00010, hubungan pemilik, KJPP; TeamsSeeder via SQL. - MasterDataSurveyorSeeder eksekusi SQL referensi (20+ tabel) via DB::unprepared . - Tambah puluhan SQL referensi (jenis, kondisi, sarana, posisi, spek, dll). - Normalisasi data inspeksi (duplikasi key dinamis), serialisasi JSON rapi. - Logging seragam ke app log + CSV error untuk audit trail.
This commit is contained in:
414
database/seeders/MigrationPermohonanSeeder.php
Normal file
414
database/seeders/MigrationPermohonanSeeder.php
Normal file
@@ -0,0 +1,414 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Lpj\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Lpj\Models\Permohonan;
|
||||
use Modules\Lpj\Models\Debiture;
|
||||
use Modules\Basicdata\Models\Branch;
|
||||
use Modules\Usermanagement\Models\User;
|
||||
|
||||
class MigrationPermohonanSeeder extends Seeder
|
||||
{
|
||||
protected $errorLogFile = __DIR__ . '/csv/permohonan/permohonan.2024.2025_error.csv';
|
||||
|
||||
public function run()
|
||||
{
|
||||
// Bersihkan file error sebelum mulai
|
||||
$this->initializeErrorLog();
|
||||
|
||||
$filePath = realpath(__DIR__ . '/csv/permohonan/permohonan.2024.2025.csv');
|
||||
|
||||
if (!$filePath) {
|
||||
Log::error('File CSV tidak ditemukan: ' . __DIR__ . '/csv/permohonan/permohonan.2024.2025.csv');
|
||||
$this->command->error('File CSV tidak ditemukan.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (($handle = fopen($filePath, 'r')) === false) {
|
||||
Log::error('Gagal membuka file CSV: ' . $filePath);
|
||||
$this->command->error('Gagal membuka file CSV.');
|
||||
return;
|
||||
}
|
||||
|
||||
$header = fgetcsv($handle, 0, ',');
|
||||
$rows = [];
|
||||
$batchSize = 1000;
|
||||
$branchCache = [];
|
||||
$debitureCache = [];
|
||||
$totalData = 0;
|
||||
$currentRow = 0;
|
||||
$batchCount = 0;
|
||||
|
||||
// Hitung total data
|
||||
while (fgetcsv($handle, 0, ',') !== false) {
|
||||
$totalData++;
|
||||
}
|
||||
|
||||
rewind($handle);
|
||||
fgetcsv($handle, 0, ','); // Lewati header
|
||||
|
||||
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));
|
||||
continue;
|
||||
}
|
||||
|
||||
$rows[] = array_combine($header, $data);
|
||||
$currentRow++;
|
||||
// print_r($rows);
|
||||
if (count($rows) >= $batchSize) {
|
||||
$batchCount++;
|
||||
$this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
|
||||
$this->processBatch($rows, $branchCache, $debitureCache, $totalData, $batchCount);
|
||||
$rows = [];
|
||||
}
|
||||
}
|
||||
|
||||
// print_r($rows);
|
||||
|
||||
if (!empty($rows)) {
|
||||
$batchCount++;
|
||||
$this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})");
|
||||
$this->processBatch($rows, $branchCache, $debitureCache, $totalData, $batchCount);
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
$this->command->info("Migrasi selesai. Total data diproses: $totalData.");
|
||||
}
|
||||
|
||||
private function processBatch(array $rows, array &$branchCache, array &$debitureCache, int $totalData, int $batchCount)
|
||||
{
|
||||
$userData = [];
|
||||
foreach ($rows as $index => $row) {
|
||||
try {
|
||||
$nomorJaminan = $row['mig_mst_jaminan_nomor_jaminan'] ?? '-';
|
||||
|
||||
// Cek apakah sudah diproses
|
||||
$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;
|
||||
}
|
||||
|
||||
// Ambil branch_id
|
||||
$branchId = $this->getBranchId($row['mig_mst_jaminan_kd_cabang'] ?? null, $branchCache);
|
||||
// if (!$branchId) {
|
||||
// $this->logError($branchId, 'Cabang tidak ditemukan');
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Ambil Debitur ID
|
||||
$debitureId = $this->getDebiturId($row['mig_mst_jaminan_kd_debitur_seq'], $debitureCache);
|
||||
if (!$debitureId) {
|
||||
$this->logError($nomorJaminan, 'Debitur tidak ditemukan');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ambil User IDs
|
||||
$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);
|
||||
|
||||
// // Ambil user IDs berdasarkan NIK
|
||||
$userIdUpdate = $this->getUserIdData($row['mig_mst_jaminan_user_create'] ?? null, $userData)['id'];
|
||||
$userIdOtorisasi = $this->getUserIdData($row['mig_mst_jaminan_user_oto'] ?? null, $userData)['id'];
|
||||
|
||||
// jika external matikan
|
||||
// if (!$userIdUpdate || !$userIdOtorisasi) {
|
||||
// $this->logError($userIdUpdate, 'Salah satu user tidak ditemukan');
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Mapping field user
|
||||
$mapUser = [
|
||||
'created_by' => $userIdUpdate,
|
||||
'updated_by' => $userIdUpdate,
|
||||
'authorized_by' => $userIdOtorisasi,
|
||||
];
|
||||
|
||||
// jika external matikan
|
||||
// if (!$userId || !$approved1Id || !$approved2Id) {
|
||||
// $this->logError($userId, 'Salah satu user tidak ditemukan');
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Mapping data
|
||||
$jenisFasilitas = $this->checkJenisFasilitas($row['mig_mst_jaminan_kd_jenis_fas_seq']);
|
||||
$tujuanPenilaian = $this->checkTujuanPenilaian($row['mig_mst_jaminan_kd_tujuan_seq']);
|
||||
$regionId = $this->checkRegion($row['mig_mst_kode_kelompok_region']);
|
||||
|
||||
|
||||
$nomor_lpj = isset($row['mig_mst_lpj_nomor_lpj']) ? $row['mig_mst_lpj_nomor_lpj'] : '';
|
||||
$nomor_lpj = is_numeric($nomor_lpj) ? (int)$nomor_lpj : 0;
|
||||
|
||||
$jenisPenilaian = $row['mig_internal_or_external'] == 1 ? 2 : 1;
|
||||
|
||||
// Simpan data
|
||||
$permohonan = Permohonan::updateOrCreate([
|
||||
'nomor_registrasi' => $nomorJaminan,
|
||||
],[
|
||||
'nomor_registrasi' => $nomorJaminan,
|
||||
'tanggal_permohonan' => $this->parseDate($row['tanggal_permohonan']),
|
||||
'user_id' => $userId['id'],
|
||||
'branch_id' => $branchId,
|
||||
'tujuan_penilaian_id' => $tujuanPenilaian,
|
||||
'debiture_id' => $debitureId,
|
||||
'jenis_fasilitas_kredit_id' => $jenisFasilitas,
|
||||
'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']),
|
||||
// end external matikan
|
||||
'keterangan' => $row['mig_mst_jaminan_catatan'] ?? null,
|
||||
'status_bayar' => 'sudah_bayar',
|
||||
'created_at' => $this->parseTimestamp($row['mig_mst_jaminan_tgl_create']),
|
||||
'updated_at' => $this->parseTimestamp($row['mig_mst_jaminan_tgl_update']),
|
||||
'mig_kd_debitur_seq' => $row['mig_mst_jaminan_kd_debitur_seq'],
|
||||
'nomor_lpj' => $nomor_lpj,
|
||||
'region_id' => $regionId,
|
||||
'jenis_penilaian_id' => $jenisPenilaian,
|
||||
'authorized_by' => $mapUser['authorized_by'],
|
||||
'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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getUserIdData(?string $code, array &$cache): array
|
||||
{
|
||||
if (!$code) {
|
||||
return ['id' => null, 'branch_id' => null];
|
||||
}
|
||||
|
||||
if (isset($cache[$code])) {
|
||||
return $cache[$code];
|
||||
}
|
||||
|
||||
$user = User::where('nik', $code)->first();
|
||||
|
||||
if ($user) {
|
||||
$cache[$code] = ['id' => $user->id, 'branch_id' => $user->branch_id];
|
||||
return $cache[$code];
|
||||
}
|
||||
|
||||
return ['id' => null, 'branch_id' => null];
|
||||
}
|
||||
|
||||
private function getUserId(string $value, array &$cache, bool $includeBranch = false): ?array
|
||||
{
|
||||
if (isset($cache[$value])) {
|
||||
return $cache[$value];
|
||||
}
|
||||
|
||||
$user = null;
|
||||
|
||||
if ($includeBranch) {
|
||||
$user = User::whereRaw('LOWER(name) = ?', [strtolower($value)])->first();
|
||||
} else {
|
||||
$user = User::where('nik', $value)->first();
|
||||
}
|
||||
|
||||
if ($user) {
|
||||
$result = ['id' => $user->id];
|
||||
if ($includeBranch) {
|
||||
$result['branch_id'] = $user->branch_id;
|
||||
}
|
||||
$cache[$value] = $result;
|
||||
return $result;
|
||||
}
|
||||
|
||||
return ['id' => null, 'branch_id' => null];
|
||||
}
|
||||
|
||||
private function getDebiturId(string $code, array &$cache): ?int
|
||||
{
|
||||
if (isset($cache[$code])) {
|
||||
return $cache[$code];
|
||||
}
|
||||
|
||||
$debitur = Debiture::where('mig_kd_debitur_seq', $code)->first();
|
||||
|
||||
if ($debitur) {
|
||||
$cache[$code] = $debitur->id;
|
||||
return $debitur->id;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private function getBranchId(?string $code, array &$cache): ?int
|
||||
{
|
||||
if (!$code) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isset($cache[$code])) {
|
||||
return $cache[$code];
|
||||
}
|
||||
|
||||
$branch = Branch::where('code', $code)->first();
|
||||
|
||||
if ($branch) {
|
||||
$cache[$code] = $branch->id;
|
||||
return $branch->id;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private function checkJenisFasilitas($code): int
|
||||
{
|
||||
// Extract the integer part before the decimal
|
||||
$intCode = (int) $code;
|
||||
|
||||
$mapping = [
|
||||
161337594516 => 1,
|
||||
161337598118 => 14,
|
||||
155739382483 => 7,
|
||||
2 => 9,
|
||||
153568936592 => 10,
|
||||
155737674431 => 11,
|
||||
161337561199 => 12,
|
||||
1 => 13,
|
||||
];
|
||||
|
||||
return $mapping[$intCode] ?? 0;
|
||||
}
|
||||
|
||||
private function checkTujuanPenilaian($code): int
|
||||
{
|
||||
$code = (int) $code;
|
||||
$mapping = [
|
||||
1 => 1,
|
||||
2 => 2,
|
||||
3 => 9,
|
||||
4 => 10,
|
||||
5 => 8,
|
||||
6 => 3,
|
||||
];
|
||||
|
||||
return $mapping[$code] ?? 1;
|
||||
}
|
||||
|
||||
private function checkRegion($code): int
|
||||
{
|
||||
$mapping = [
|
||||
'01' => 1,
|
||||
'02' => 2,
|
||||
'04' => 3,
|
||||
'07' => 4,
|
||||
'06' => 5,
|
||||
];
|
||||
|
||||
return $mapping[$code] ?? 1;
|
||||
}
|
||||
|
||||
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();
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
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();
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Gagal memparsing timestamp: ' . $timestamp . '. Pesan: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function initializeErrorLog()
|
||||
{
|
||||
$file = $this->errorLogFile;
|
||||
|
||||
// Hapus file lama jika ada
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
// Buat file baru dengan header
|
||||
$handle = fopen($file, 'w');
|
||||
fputcsv($handle, ['mig_mst_jaminan_nomor_jaminan', 'Error']);
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
private function logError($nomorJaminan, string $message)
|
||||
{
|
||||
Log::error("Error migrasi permohonan [$nomorJaminan]: $message");
|
||||
|
||||
// Tulis ke file error
|
||||
$handle = fopen($this->errorLogFile, 'a');
|
||||
fputcsv($handle, [$nomorJaminan, $message]);
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user