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); } }