initializeErrorLog(); // Path ke file csv $filePath = realpath(__DIR__ . '/csv/penawaran/mig_penawaran_external.csv'); if (!$filePath) { Log::error('File csv tidak ditemukan: ' . __DIR__ . '/csv/permohonan/mig_penawaran_external.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; // Ukuran batch $userDataChace = []; $nomorRegisCahce = []; $errorCount = 0; // Inisialisasi variabel errorCount $errorDebitureIds = []; // Inisialisasi array errorDebitureIds $totalData = 0; // 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; 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++; // Jika sudah mencapai batch size, proses batch if (count($rows) >= $batchSize) { $batchCount++; $this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})"); $this->processBatch($rows, $nomorRegisCahce, $userDataChace, $errorCount, $errorDebitureIds, $totalData, $batchCount, $currentRow); $rows = []; } } // Proses sisa data jika ada // print_r($rows[0]); if (!empty($rows)) { $batchCount++; $this->command->info("Memproses batch ke-{$batchCount} ({$currentRow}/{$totalData})"); $this->processBatch($rows, $nomorRegisCahce, $userDataChace, $errorCount, $errorDebitureIds, $totalData, $batchCount, $currentRow); } fclose($handle); $this->command->info("Data debiture berhasil dimigrasikan. Total data: {$totalData}, Total batch: {$batchCount}, Total error: {$errorCount}"); } private function processBatch( array $rows, array &$userDataChace, array &$nomorRegisCahce, int &$errorCount, array &$errorDebitureIds, int $totalData, int $batchCount, int $currentRow ) { $userData = []; foreach ($rows as $index => $row) { try { // Jalankan setiap baris dengan transaksi sendiri DB::beginTransaction(); // Cek apakah sudah diproses $existingRecord = PenawaranTender::where('nomor_registrasi', $row['mig_nomor_jaminan'])->first(); if ($existingRecord && $existingRecord->created_at) { $this->command->info('Data sudah diproses sebelumnya: ' . $row['mig_nomor_jaminan']); continue; } // Ambil nomor registrasi $nomor_registrasi = $this->getNomorRegistrasiPermohonan($row['mig_nomor_jaminan'], $nomorRegisCahce); if (!$nomor_registrasi) { throw new \Exception("Nomor registrasi tidak valid"); } $userIdUpdate = $this->getUserIdData($row['mig_created_by'] ?? null, $userData)['id']; $userIdOtorisasi = $this->getUserIdData($row['mig_authorized_by'] ?? null, $userData)['id']; 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, ]; // create random code $tgl = $this->parseTimestamp($row['mig_tgl_penyerahan']); // ambil nilai tahun terakhir contoh 2023 maka ambil 23 $year = date('y', strtotime($tgl)); // random code berdasarkan tgl contoh NP2300001 $code = 'NP' . $year . str_pad(rand(1, 99999), 5, '0', STR_PAD_LEFT); // outputnya NP2300001: // Cek apakah kode sudah ada $existingCode = PenawaranTender::where('code', $code)->first(); if ($existingCode) { $code = 'NP' . $year . str_pad(rand(1, 99999), 5, '0', STR_PAD_LEFT); } // Buat penilaian $penilaian = PenawaranTender::create([ 'code' => $code, 'nama_kjpp_sebelumnya' => $row['mig_nama_kjpp'], 'biaya_kjpp_sebelumnya' => $row['mig_tot_jasa'], 'tanggal_penilaian_sebelumnya' => $this->parseTimestamp($row['mig_tgl_penyerahan']), 'nomor_registrasi' => $nomor_registrasi, 'tujuan_penilaian_kjpp_id' => $this->checkTujuanPenilaian($row['mig_mst_jaminan_kd_tujuan_seq']), 'jenis_laporan_id' => 1, 'start_date' => $this->parseTimestamp($row['mig_tgl_penyerahan']), 'end_date' => $this->parseTimestamp($row['mig_terima_laporan']), 'catatan' => $row['mig_catatan'], 'status' => 'spk', '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'], ]); // Commit transaksi DB::commit(); $this->command->info('Proses data penilaian ' . $row['mig_nomor_jaminan'] . ' (' . ($index + 1) . '/' . count($rows) . ')'); } catch (\Exception $e) { // Rollback jika ada error DB::rollBack(); Log::error('Error pada baris: ' . json_encode($row) . '. Pesan: ' . $e->getMessage()); $errorCount++; $errorDebitureIds[] = $row['mig_team_id'] ?? '-'; $this->logError($row['mig_team_id'] ?? '-', $e->getMessage()); continue; } } $this->command->info("Batch {$batchCount} selesai. Total error: " . count($errorDebitureIds)); } private function getNomorRegistrasiPermohonan($nomor_registrasi_id, $nomorRegisCahce) { if (isset($nomorRegisCahce[$nomor_registrasi_id])) { return $nomorRegisCahce[$nomor_registrasi_id]; } $nomorRegis = Permohonan::where('nomor_registrasi', $nomor_registrasi_id)->first(); if (!$nomorRegis) { return null; } $nomorRegisCahce[$nomor_registrasi_id] = $nomorRegis->nomor_registrasi; return $nomorRegis->nomor_registrasi; } 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($mig_user_id, $userDataChace) { if (isset($userDataChace[$mig_user_id])) { return $userDataChace[$mig_user_id]; } $userId = User::where('nik', $mig_user_id)->first(); if (!$userId) { return null; } $userDataChace[$mig_user_id] = $userId->id; return $userId->id; } 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() { // 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); } private function checkTujuanPenilaian($code): int { $mapping = [ 1 => 7, 2 => 5, 3 => 8, 6 => 6, ]; return $mapping[$code] ?? 7; } }