- 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.
307 lines
10 KiB
PHP
307 lines
10 KiB
PHP
<?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\PenawaranTender;
|
|
use Modules\Usermanagement\Models\User;
|
|
use Modules\Lpj\Models\Penilaian;
|
|
use Modules\Lpj\Models\PenilaianTeam;
|
|
|
|
class MigExternalPenawaranSeeder extends Seeder
|
|
{
|
|
protected $errorLogFile = __DIR__ . '/csv/penawaran/mig_penawaran_external_error.csv';
|
|
|
|
/**
|
|
* Run the database seeds.
|
|
*/
|
|
public function run()
|
|
{
|
|
// Bersihkan/Inisialisasi file error log
|
|
$this->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;
|
|
}
|
|
}
|