initializeErrorLog(); // Path ke file csv $filePath = realpath(__DIR__ . '/csv/dokumen-dan-pemilik/mig_pemilik_dan_dokument.csv'); if (!$filePath) { Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/mig_pemilik_dan_dokument.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; // Ukuran batch $permohonanCache = []; $jenisJaminanCache = []; $pemilikJaminanCache = []; $provinceCache = []; $cityCache = []; $districtCache = []; $subdistrictCache = []; $totalData = 0; $errorCount = 0; $errorDebitureIds = []; $hubunganPemilikCache = []; // Menghitung total data di file CSV while (($data = fgetcsv($handle, 0, '~')) !== false) { $totalData++; } rewind($handle); // Reset pointer ke awal file fgetcsv($handle, 0, '~'); // Skip header $batchCount = 0; $currentRow = 0; // Membaca setiap baris dalam CSV 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++; $errorDebitureIds[] = $data[0] ?? 'ID tidak valid'; // Menyimpan ID yang error continue; } $rows[] = array_combine($header, $data); $currentRow++; // print_r($rows); if (count($rows) >= $batchSize) { $errorDebitureIds[] = $data[0] ?? 'ID tidak valid'; // Menyimpan ID yang error $this->processBatch($rows, $permohonanCache, $jenisJaminanCache, $pemilikJaminanCache, $provinceCache, $cityCache, $districtCache, $subdistrictCache, $batchCount, $currentRow, $totalData, $errorCount, $errorDebitureIds, $hubunganPemilikCache); $rows = []; } } // print_r($rows[0]); if (!empty($rows)) { $this->processBatch($rows, $permohonanCache, $jenisJaminanCache, $pemilikJaminanCache, $provinceCache, $cityCache, $districtCache, $subdistrictCache, $batchCount, $currentRow, $totalData, $errorCount, $errorDebitureIds, $hubunganPemilikCache); } fclose($handle); $this->command->info('Data debiture berhasil dimigrasikan.'); } /** * Proses batch data. */ private function processBatch( array $rows, array &$permohonanCache, array &$jenisJaminanCache, array &$pemilikJaminanCache, array &$provinceCache, array &$cityCache, array &$districtCache, array &$subdistrictCache, int $batchCount, int $currentRow, int $totalData, int &$errorCount, array &$errorDebitureIds, array &$hubunganPemilikCache ) { foreach ($rows as $index => $row) { try { // Jalankan transaksi per-baris DB::beginTransaction(); // Cari permohonan $permohonan = $this->getPermohonanId( $row['mig_kd_debitur_seq'], $row['mig_nomor_jaminan'], $permohonanCache ); if (empty($permohonan['debiture_id'])) { throw new \Exception('Missing debiture_id'); } // 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"); } // Ambil lokasi $proviceCode = $this->getProvinceCode($row['mig_province_name'], $provinceCache); $cityCode = $this->getCityCode($row['mig_city_name'], $cityCache); $districtCode = $this->getDistrictCode($row['mig_district_name'], $districtCache); $subdistrict = $this->getSubdistrictCode($row['mig_village_name'], $subdistrictCache, $districtCache); $hubunganPemilik = $this->getHubunganPemilikJaminanId($row['mig_hubungan_pemilik_jaminan'], $hubunganPemilikCache); // Buat Pemilik Jaminan $pemilik_jaminan = PemilikJaminan::create([ 'debiture_id' => $permohonan['debiture_id'], 'hubungan_pemilik_jaminan_id' => $hubunganPemilik, 'name' => $row['name'], 'detail_sertifikat' => null, 'npwp' => null, 'nomor_id' => null, 'email' => null, 'phone' => null, '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(), 'is_mig' => 1 ]); // Buat Dokumen Jaminan DokumenJaminan::create([ 'debiture_id' => $permohonan['debiture_id'], 'permohonan_id' => $permohonan['id'], 'jenis_jaminan_id' => 17, 'pemilik_jaminan_id' => $pemilik_jaminan->id, '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(), 'is_mig' => 1 ]); // Commit jika semua sukses 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'] ?? '-'; } } $this->command->info("Batch {$batchCount} selesai. Total error: " . count($errorDebitureIds)); } private function getPermohonanId(string $code, string $mig_nomor_jaminan, array &$cache): ?array { if (isset($cache[$code])) { return $cache[$code]; } $permohonan = Permohonan::where('mig_kd_debitur_seq', $code)->where('nomor_registrasi', $mig_nomor_jaminan)->first(); if ($permohonan) { $cache[$code] = [ 'id' => $permohonan->id, 'debiture_id' => $permohonan->debiture_id, 'mig_kd_debitur_seq' => $permohonan->mig_kd_debitur_seq ]; return $cache[$code]; } return [ 'id' => null, 'debiture_id' => null, 'mig_kd_debitur_seq' => null ]; } private function getJaminanId(string $code, array &$cache): ?int { if (isset($cache[$code])) { return $cache[$code]; } $jaminan = JenisJaminan::whereRaw('LOWER(name) = ?', [strtolower($code)])->first(); if ($jaminan) { $cache[$code] = $jaminan->id; return $jaminan->id; } return null; } private function getPemilikJaminanId(string $code, array &$cache): ?int { if (isset($cache[$code])) { return $cache[$code]; } $jaminan = PemilikJaminan::where('mig_kd_debitur_seq', $code)->first(); if ($jaminan) { $cache[$code] = $jaminan->id; return $jaminan->id; } return 1; } private function getDebitureId(string $code, array &$cache): ?int { if (isset($cache[$code])) { return $cache[$code]; } $debiture = Debiture::where('mig_kd_debitur_seq', $code)->first(); if ($debiture) { $cache[$code] = $debiture->id; return $debiture->id; } return null; } private function getProvinceCode(string $name, array &$cache): ?string { $normalizedName = strtolower($name); if (isset($cache[$normalizedName])) { return $cache[$normalizedName]; } $province = Province::whereRaw('LOWER(name) = ?', [strtolower($name)])->first(); if ($province) { $cache[$normalizedName] = $province->code; return $province->code; } return null; } private function getCityCode(string $name, array &$cache): ?string { $normalizedName = strtolower($name); if (isset($cache[$normalizedName])) { return $cache[$normalizedName]; } $city = City::whereRaw('LOWER(name) = ?', [strtolower($name)])->first(); if ($city) { $cache[$normalizedName] = $city->code; return $city->code; } return null; } private function getDistrictCode(string $name, array &$cache): ?string { $normalizedName = strtolower($name); if (isset($cache[$normalizedName])) { return $cache[$normalizedName]; } $district = District::whereRaw('LOWER(name) = ?', [strtolower($name)])->first(); if ($district) { $cache[$normalizedName] = $district->code; return $district->code; } return null; } private function getSubdistrictCode(string $name, array &$cache, array &$districtCache): ?array { $normalizedName = strtolower($name); // Pastikan cache menyimpan array, bukan hanya kode if (isset($cache[$normalizedName])) { return $cache[$normalizedName]; } // Ambil subdistrict dari database $subdistrict = Village::whereRaw('LOWER(name) = ?', [$normalizedName])->first(); // Jika ditemukan, simpan ke dalam cache sebagai array lengkap if ($subdistrict) { $cache[$normalizedName] = [ 'code' => $subdistrict->code, 'postal_code' => $subdistrict->postal_code ]; return $cache[$normalizedName]; } // Jika tidak ditemukan, kembalikan null return [ 'code' => null, 'postal_code' => null ]; } private function getHubunganPemilikJaminanId(string $code, array &$cache): ?int { if (isset($cache[$code])) { return $cache[$code]; } $jaminan = HubunganPemilikJaminan::whereRaw('LOWER(name) = ?', [strtolower($code)])->first(); if ($jaminan) { $cache[$code] = $jaminan->id; return $jaminan->id; } return null; } /** * Mengonversi nilai TIMESTAMP menjadi format datetime yang valid. */ 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) 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; } } private function initializeErrorLog() { $file = $this->errorLogFile; if (file_exists($file)) { unlink($file); // Hapus file lama } $handle = fopen($file, 'w'); fputcsv($handle, ['mig_kd_debitur_seq', 'Error']); fclose($handle); } private function logError(string $kode, string $message) { Log::error("Error migrasi dokumen jaminan [$kode]: $message"); $handle = fopen($this->errorLogFile, 'a'); fputcsv($handle, [$kode, $message]); fclose($handle); } }