initializeErrorLog(); // Path ke file csv $filePath = realpath(__DIR__ . '/csv/debitures/debitur.latest.csv'); if (!$filePath) { Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/debitures/debitur.latest.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 = 500; $userData = []; $branchCache = []; $totalData = 0; // Hitung total baris while (($data = fgetcsv($handle, 0, '|')) !== false) { $totalData++; } rewind($handle); fgetcsv($handle, 0, '|'); // Lewati header $currentRow = 0; $batchCount = 0; while (($data = fgetcsv($handle, 0, '|')) !== false) { if (count($data) != count($header)) { $this->logError($data[0] ?? '-', 'Jumlah kolom tidak sesuai'); continue; } $rows[] = array_combine($header, $data); $currentRow++; if (count($rows) >= $batchSize) { $batchCount++; $this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})"); $this->processBatch($rows, $branchCache, $userData); $rows = []; } } // print_r($rows); if (!empty($rows)) { $batchCount++; $this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})"); $this->processBatch($rows, $branchCache, $userData); } fclose($handle); $this->command->info("Data debiture berhasil dimigrasikan. Total data: {$totalData}, Total batch: {$batchCount}"); } private function processBatch(array $rows, array &$branchCache, array &$userData) { foreach ($rows as $index => $row) { try { $kode = $row['mig_kd_debitur_seq'] ?? '-'; // Cek apakah sudah diproses sebelumnya $existingRecord = Debiture::where('mig_kd_debitur_seq', $kode)->first(); if ($existingRecord && $existingRecord->processed_at) { $this->command->info("Data sudah diproses sebelumnya: $kode"); //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; } // Ambil user IDs berdasarkan NIK $userIdUpdate = $this->getUserId($row['mig_user_update'] ?? null, $userData)['id']; $userIdOtorisasi = $this->getUserId($row['mig_user_oto'] ?? null, $userData)['id']; // if (!$userIdUpdate || !$userIdOtorisasi) { // $this->logError($kode, 'Salah satu user tidak ditemukan'); // continue; // } // Mapping field user $mapUser = [ 'created_by' => $userIdUpdate, 'updated_by' => $userIdUpdate, 'authorized_by' => $userIdOtorisasi, ]; // Parsing nomor HP $nomorHp = !empty($row['mig_phone']) ? preg_replace('/[^0-9]/', '', $row['mig_phone']) : null; $email = !empty($row['email']) ? $row['email'] : null; $nomorId = !empty($row['nomor_id']) ? $row['nomor_id'] : null; // Parsing waktu $authorizedAt = $this->parseTimestamp($row['mig_tgl_oto'] ?? null); $createdAt = $this->parseTimestamp($row['created_at'] ?? null); $updatedAt = $this->parseTimestamp($row['updated_at'] ?? null); if (!$createdAt || !$updatedAt) { $this->logError($kode, 'Gagal parsing created_at / updated_at'); continue; } // Simpan data Debiture::updateOrCreate([ 'mig_kd_debitur_seq' => (int) strtok($row['mig_kd_debitur_seq'], '.'), ], [ 'branch_id' => $branchId, 'name' => $row['name'] ?? '', 'cif' => (int) strtok($row['cif'],'.') ?: '0000000000', 'phone' => $nomorHp, 'nomor_id' => $nomorId, 'email' => $email, 'npwp' => null, 'address' => $row['address'] ?? null, 'authorized_at' => $authorizedAt, 'authorized_by' => $mapUser['authorized_by'], 'created_by' => $mapUser['created_by'] ?? null, 'updated_by' => $mapUser['updated_by'] ?? null, 'created_at' => $createdAt, 'updated_at' => $updatedAt, 'mig_kd_debitur_seq' => (int) strtok($row['mig_kd_debitur_seq'], '.'), 'processed_at' => now(), 'mig_debitur' => json_encode($row), 'is_mig' => 1 ]); $this->command->info("Proses data debiture $kode (" . ($index + 1) . '/' . count($rows) . ')'); } catch (\Exception $e) { $this->logError($row['mig_kd_debitur_seq'] ?? '-', "Error eksepsi: " . $e->getMessage()); } } } 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 getUserId(?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 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; } catch (\Exception $e) { Log::error('Gagal memparsing timestamp: ' . $timestamp . '. Pesan: ' . $e->getMessage()); return null; } } private function initializeErrorLog() { // Jika file lama ada, hapus if (file_exists($this->errorLogFile)) { unlink($this->errorLogFile); } // Buat file baru dengan header $handle = fopen($this->errorLogFile, 'w'); fputcsv($handle, ['mig_kd_debitur_seq', 'Error']); fclose($handle); } private function logError(string $kode, string $message) { // Catat ke log Log::error("Error migrasi debiture [$kode]: $message"); // Tulis ke file error $handle = fopen($this->errorLogFile, 'a'); fputcsv($handle, [$kode, $message]); fclose($handle); } }